set.seed(123)
Homoscedastic Error Variances
For the visualization of homocedastic and hetercedastic variance, we
simulate:
\[
y \sim N \left ( -1 + 2x, 1 \right )
\]
The funnel-shaped heterocedasticy is simulated using:
\[
y \sim N \left ( -1 + 2x, \left ( 0.1 + 0.3 \left ( x + 3 \right )
\right )^{2} \right )
\]
par(mfrow = c(2, 2))
# Homecedastic variance
X <- seq(-2, 2, length.out = 300)
homecedastic_random <- function (x){
rnorm(1, mean = -1 + 2 * x, sd = 1)[1]
}
homo_y <- sapply(X, homecedastic_random)
homo_e <- (-1 + 2 * X) - homo_y
plot(X, homo_y, xlab='', ylab='', main='homocedastic variance')
abline(-1, 2, col = "red", lwd=2)
plot(X, homo_e, xlab='', ylab='', main='homocedastic variance errors')
abline(0, 0, col = "red", lwd=2)
heterocedastic_random <- function (x){
rnorm(1, mean = -1 + 2 * x, sd = 0.1 + 0.3 * (x + 3))[1]
}
hetero_y <- sapply(X, heterocedastic_random)
hetero_e <- (-1 + 2 * X) - hetero_y
plot(X, hetero_y, xlab='', ylab='', main='funnel-shaped heterocedastic variance')
abline(-1, 2, col = "red", lwd=2)
plot(X, hetero_e, xlab='', ylab='', main='funnel-shaped heterocedastic variance errors')
abline(0, 0, col = "red", lwd=2)

Example 3.1 Munich Rent Index—Heteroscedastic Variances
par(mfrow = c(1, 2))
munich_rent_url <- "https://www.uni-goettingen.de/de/document/download/64c29c1b1fccb142cfa8f29a942a9e05.raw/rent99.raw"
munich_rent_index <- read.table(
url(munich_rent_url),
header = 1,
colClasses = c(
"numeric", "numeric", "numeric",
"numeric", "factor", "factor",
"factor", "factor", "factor"
)
)
simple_reg <- lm(rent ~ area, data = munich_rent_index)
plot(munich_rent_index$area, munich_rent_index$rent, xlab = 'area in sqm', ylab = 'net rent in Euro')
abline(simple_reg, col = "red", lwd = 2)
predicted <- predict(simple_reg)
plot(munich_rent_index$area, predicted - munich_rent_index$rent, xlab = 'area in sqm', ylab = 'residuals')
abline(0, 0, col = "red", lwd = 2)

Uncorrelated Errors
Simulated autocorrelated errors with positive correlation are
generated using:
\[
y_{i} = -1 + 2 x_{i} + \epsilon_{i}
\] \[
\text{Where } \epsilon_{i} = 0.9 \epsilon_{i - 1} + \mu_{i} \wedge
\mu_{i} \sim N(0, 0.5^{2})
\]
par(mfrow = c(1, 2))
n <- 100
X <- seq(-3, 3, length.out = n)
mu <- rnorm(n, mean = 0, sd = 0.5)
epsilon <- mu[1]
for(i in seq(2, n)){
epsilon <- c(epsilon, 0.9 * epsilon[i - 1] + mu[i])
}
y_pred <- -1 + 2 * X + epsilon
plot(X, y_pred, xlab = 'x', ylab = '', main = 'observations and regression line')
abline(-1, 2, col = "red", lwd = 2)
plot(epsilon, xlab = 'i', ylab = '', main = 'time series of the errors')
abline(0, 0, col = "red", lwd = 2)

Simulated autocorrelated errors with negative correlation are
generated using:
\[
y_{i} = -1 + 2 x_{i} + \epsilon_{i}
\] \[
\text{Where } \epsilon_{i} = - 0.9 \epsilon_{i - 1} + \mu_{i} \wedge
\mu_{i} \sim N(0, 0.5^{2})
\]
par(mfrow = c(1, 2))
n <- 100
X <- seq(-3, 3, length.out = n)
mu <- rnorm(n, mean = 0, sd = 0.5)
epsilon <- mu[1]
for(i in seq(2, n)){
epsilon <- c(epsilon, -0.9 * epsilon[i - 1] + mu[i])
}
y_pred <- -1 + 2 * X + epsilon
plot(X, y_pred, xlab = 'x', ylab = '', main = 'observations and regression line')
abline(-1, 2, col = "red", lwd = 2)
plot(epsilon, xlab = 'i', ylab = '', main = 'time series of the errors')
abline(0, 0, col = "red", lwd = 2)

Mispecified model is simulated using:
\[
y_{i} = \sin(x_{i}) + x_{i} + \epsilon_{i}
\] \[
\text{Where } \epsilon_{i} \sim N(0, 0.3^{2})
\]
par(mfrow = c(2, 2))
n <- 100
X <- seq(-3, 3, length.out = n)
epsilon <- rnorm(n, mean = 0, sd = 0.3)
y <- sin(X) + X + epsilon
plot(X, y, xlab = '', ylab = '', main = 'observations and true function')
lines(X, sin(X) + X, col = "red", lwd = 2)
linear_model <- lm(y ~ X)
plot(X, y, xlab = '', ylab = '', main = 'observations and regression line')
abline(linear_model, col = "red", lwd = 2)
y_pred <- predict(linear_model, data = X)
res <- y_pred - y
plot(X, res, xlab = '', ylab = '', main = 'residuals')
abline(0, 0, col = "red", lwd = 2)

Additivity of Errors
We simulate data using:
\[
y_{i} = \exp(1 + x_{i1} - x_{i2} + \epsilon_{i})
\]
With: \[
\epsilon_{i} \sim N \left ( 0, {0.4}^{2} \right )
\]
We plot this model:
par(mfrow = c(2, 2))
n <- 50
x1 <- seq(0, 3, length.out = n)
x2 <- seq(0, 3, length.out = n)
x_grid <- expand.grid(x1 = x1, x2 = x2)
epsilon <- rnorm(n^2, mean = 0, sd = 0.4)
y <- exp(1 + x_grid$x1 - x_grid$x2 + epsilon)
plot(
x_grid$x1, y,
main = 'scatter plot: y versus x1',
xlab = 'x1',
ylab = 'y'
)
plot(
x_grid$x2, y,
main = 'scatter plot: y versus x2',
xlab = 'x2',
ylab = 'y'
)
plot(
x_grid$x1, log(y),
main = 'scatter plot: log(y) versus x1',
xlab = 'x1',
ylab = 'log(y)'
)
plot(
x_grid$x2, log(y),
main = 'scatter plot: log(y) versus x2',
xlab = 'x2',
ylab = 'log(y)'
)

Example 3.2 Supermarket Scanner Data
Data not available online.
Example 3.3 Munich Rent Index — Variable Transformation
Importing data using script:
source("import_data/munich_rent_index.R")
We do the regression model:
\[
\text{rentsqm}_{i} = \beta_{0} + \beta_{1} \cdot f(\text{area}_{i}) +
\epsilon_{i}
\]
With both \(f(\cdot)\):
\[
f(\text{area}_{i}) = \text{area}_{i}
\]
For linear model and:
\[
f(\text{area}_{i}) = \frac{1}{\text{area}_{i}}
\]
For future use we define those models:
Model 1 (\(M1\)):
\[
\mathbb{E}(\text{rentqsm}) = \beta_{0} + \beta_{1} \text{area}
\]
Model 2 (\(M2\)):
\[
\mathbb{E}(\text{rentqsm}) = \beta_{0} + \beta_{1} \frac{1}{\text{area}}
\]
For nonlinear relationship between \(\text{rentsqm}\) and \(\text{area}\). The plotted graph below are
those two models with the left panels the linear regression without the
transformation and the right panels with the inverse transformation.
par(mfrow = c(3, 2))
m1 <- lm(rentsqm ~ area, data = munich_rent_index)
summary(m1)
Call:
lm(formula = rentsqm ~ area, data = munich_rent_index)
Residuals:
Min 1Q Median 3Q Max
-6.9622 -1.5737 -0.1102 1.5861 9.9674
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 9.46883 0.12426 76.20 <2e-16 ***
area -0.03499 0.00174 -20.11 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.291 on 3080 degrees of freedom
Multiple R-squared: 0.1161, Adjusted R-squared: 0.1158
F-statistic: 404.5 on 1 and 3080 DF, p-value: < 2.2e-16
m2 <- lm(rentsqm ~ I(1 / area), data = munich_rent_index)
summary(m2)
Call:
lm(formula = rentsqm ~ I(1/area), data = munich_rent_index)
Residuals:
Min 1Q Median 3Q Max
-7.3963 -1.5733 -0.0921 1.5824 10.1287
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 4.7321 0.1084 43.65 <2e-16 ***
I(1/area) 140.1783 5.9287 23.64 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.241 on 3080 degrees of freedom
Multiple R-squared: 0.1536, Adjusted R-squared: 0.1533
F-statistic: 559 on 1 and 3080 DF, p-value: < 2.2e-16
plot_rentsqm_area <- function(title) {
plot(
munich_rent_index$area,
munich_rent_index$rentsqm,
main = title,
xlab = 'area in sqm',
ylab = 'rent per sqm'
)
}
seq_area <- seq(min(munich_rent_index$area), max(munich_rent_index$area), length.out = 100)
plot_rentsqm_area('rent per sqm vs. area')
m1_beta <- m1$coefficients
lines(seq_area, m1_beta[1] + m1_beta[2] * seq_area, col = 'red', lwd = 2)
plot_rentsqm_area('f(area) = 1/area')
m2_beta <- m2$coefficients
lines(seq_area, m2_beta[1] + m2_beta[2] * 1 / seq_area, col = 'red', lwd = 2)
plot_residuals <- function(model) {
plot(
munich_rent_index$area,
model$residuals,
main = 'residuals',
xlab = 'area in sqm',
ylab = 'residuals'
)
abline(0, 0, col = 'red', lwd = 2)
}
plot_residuals(m1)
plot_residuals(m2)
plot_av_residuals <- function(model) {
av_residuals <- tapply(
model$residuals, munich_rent_index$area,
mean
)
plot(
unique(munich_rent_index$area),
av_residuals,
main = 'average residuals',
xlab = 'area in sqm',
ylab = 'residuals'
)
abline(0, 0, col = 'red', lwd = 2)
}
plot_av_residuals(m1)
plot_av_residuals(m2)

Example 3.4 Munich Rent Index — Polynomial Regression
For polynomial regressions we adjust two different model. Of
second-order (Model 3 (\(M3\))):
\[
\mathbb{E}(\text{rentqsm}) = \beta_{0} + \beta_{1} \text{area} +
\beta_{2} \text{area}^{2}
\]
And third-order (Model 4 (\(M4\))):
\[
\mathbb{E}(\text{rentqsm}) = \beta_{0} + \beta_{1} \text{area} +
\beta_{2} \text{area}^{2} + \beta_{3} \text{area}^{3}
\]
par(mfrow = c(2, 2))
m3 <- lm(rentsqm ~ area + I(area^2), data = munich_rent_index)
summary(m3)
Call:
lm(formula = rentsqm ~ area + I(area^2), data = munich_rent_index)
Residuals:
Min 1Q Median 3Q Max
-7.2150 -1.5816 -0.0915 1.5869 9.9392
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.183e+01 2.684e-01 44.081 <2e-16 ***
area -1.057e-01 7.351e-03 -14.380 <2e-16 ***
I(area^2) 4.707e-04 4.758e-05 9.892 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.255 on 3079 degrees of freedom
Multiple R-squared: 0.1433, Adjusted R-squared: 0.1428
F-statistic: 257.6 on 2 and 3079 DF, p-value: < 2.2e-16
m4 <- lm(rentsqm ~ area + I(area^2) + I(area^3), data = munich_rent_index)
summary(m4)
Call:
lm(formula = rentsqm ~ area + I(area^2) + I(area^3), data = munich_rent_index)
Residuals:
Min 1Q Median 3Q Max
-7.4269 -1.5854 -0.1101 1.5948 10.0670
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 1.428e+01 5.730e-01 24.915 < 2e-16 ***
area -2.175e-01 2.432e-02 -8.946 < 2e-16 ***
I(area^2) 1.981e-03 3.167e-04 6.254 4.54e-10 ***
I(area^3) -6.105e-06 1.266e-06 -4.823 1.49e-06 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.247 on 3078 degrees of freedom
Multiple R-squared: 0.1497, Adjusted R-squared: 0.1489
F-statistic: 180.7 on 3 and 3078 DF, p-value: < 2.2e-16
plot_rentsqm_area('rent per sqm vs. area')
m3_beta <- m3$coefficients
lines(seq_area, m3_beta[1] + m3_beta[2] * seq_area + m3_beta[3] * seq_area ^ 2, col = 'red', lwd = 2)
plot_rentsqm_area('f(area) = 1/area')
m4_beta <- m4$coefficients
lines(seq_area, m4_beta[1] + m4_beta[2] * seq_area + m4_beta[3] * seq_area ^ 2 + m4_beta[4] * seq_area ^ 3, col = 'red', lwd = 2)
plot_residuals(m3)
plot_residuals(m4)

Example 3.5 Munich Rent Index — Additive Models
We define the following aditive models. Additive model 1:
\[
\mathcal{E}(\text{rentsqm}_{i}) = \beta_{0} + \beta_{1} \cdot
\frac{1}{\text{area}_{i}} + \beta_{2} \cdot \text{yearc}_{i}
\]
And a second one with \(\text{yearc}\) as a polinomial of degree
3:
\[
\mathcal{E}(\text{rentsqm}_{i}) = \beta_{0} + \beta_{1} \cdot
\frac{1}{\text{area}_{i}} + \beta_{2} \cdot \text{yearc}_{i} + \beta_{3}
\cdot {\text{yearc}_{i}}^{2} + \beta_{4} \cdot {\text{yearc}_{i}}^{3}
\]
additive_m1 <- lm(rentsqm ~ I(1/area) + yearc, data = munich_rent_index)
summary(additive_m1)
Call:
lm(formula = rentsqm ~ I(1/area) + yearc, data = munich_rent_index)
Residuals:
Min 1Q Median 3Q Max
-7.2682 -1.4894 -0.1401 1.3935 9.0582
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -65.406008 3.351088 -19.52 <2e-16 ***
I(1/area) 119.360735 5.636182 21.18 <2e-16 ***
yearc 0.036033 0.001721 20.94 <2e-16 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.097 on 3079 degrees of freedom
Multiple R-squared: 0.2591, Adjusted R-squared: 0.2586
F-statistic: 538.5 on 2 and 3079 DF, p-value: < 2.2e-16
additive_m2 <- lm(rentsqm ~ I(1/area) + yearc + I(yearc ^ 2)+ I(yearc ^ 3), data = munich_rent_index)
summary(additive_m2)
Call:
lm(formula = rentsqm ~ I(1/area) + yearc + I(yearc^2) + I(yearc^3),
data = munich_rent_index)
Residuals:
Min 1Q Median 3Q Max
-6.9920 -1.3705 -0.1354 1.3711 8.2834
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.942e+04 2.993e+04 0.983 0.326
I(1/area) 1.296e+02 5.536e+00 23.406 <2e-16 ***
yearc -4.330e+01 4.592e+01 -0.943 0.346
I(yearc^2) 2.119e-02 2.348e-02 0.902 0.367
I(yearc^3) -3.445e-06 4.002e-06 -0.861 0.389
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.039 on 3077 degrees of freedom
Multiple R-squared: 0.3, Adjusted R-squared: 0.2991
F-statistic: 329.7 on 4 and 3077 DF, p-value: < 2.2e-16
A third model is specified using the truncated year of construction
(\(\text{yearc} - 1900\)):
munich_rent_index$yearco <- munich_rent_index$yearc - 1900
additive_m3 <- lm(rentsqm ~ I(1/area) + yearco + I(yearco ^ 2)+ I(yearco ^ 3), data = munich_rent_index)
summary(additive_m3)
Call:
lm(formula = rentsqm ~ I(1/area) + yearco + I(yearco^2) + I(yearco^3),
data = munich_rent_index)
Residuals:
Min 1Q Median 3Q Max
-6.9920 -1.3705 -0.1354 1.3711 8.2834
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 5.417e+00 4.779e-01 11.335 < 2e-16 ***
I(1/area) 1.296e+02 5.536e+00 23.406 < 2e-16 ***
yearco -9.434e-02 3.384e-02 -2.788 0.00534 **
I(yearco^2) 1.553e-03 6.728e-04 2.308 0.02105 *
I(yearco^3) -3.445e-06 4.002e-06 -0.861 0.38947
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.039 on 3077 degrees of freedom
Multiple R-squared: 0.3, Adjusted R-squared: 0.2991
F-statistic: 329.7 on 4 and 3077 DF, p-value: < 2.2e-16
As in the book, we show the effect of year of construction in the
polynomial of degree 3:
par(mfrow = c(1, 2))
beta_add_m2 <- additive_m2$coefficients
yearc_seq <- seq(min(munich_rent_index$yearc), max(munich_rent_index$yearc), length.out = 100)
plot_effect <- function(beta, year_seq, title){
plot(
year_seq,
beta[3] * year_seq + beta[4] * year_seq^2 + beta[5] * year_seq^3,
main = title,
xlab = 'year of construction',
ylab = 'effect of year of construction',
type = 'l',
col = 'darkblue',
lwd = 2
)
}
plot_effect(beta_add_m2, yearc_seq, 'effect of year of construction')
beta_add_m3 <- additive_m3$coefficients
truncate_yearc_seq <- seq(min(munich_rent_index$yearco), max(munich_rent_index$yearco), length.out = 100)
plot_effect(beta_add_m3, truncate_yearc_seq, 'effect of year of construction, coded from 18 to 96')

A new model is defined using the orthogonal polynomial of year of
construction:
munich_rent_index$areainv <- (1 / munich_rent_index$area) - mean(1 / munich_rent_index$area)
poly_year <- poly(munich_rent_index$yearco, 3)
munich_rent_index$yearcc <- poly_year[, 1]
munich_rent_index$yearcc2 <- poly_year[, 2]
munich_rent_index$yearcc3 <- poly_year[, 3]
additive_m4 <- lm(rentsqm ~ areainv + yearcc + yearcc2 + yearcc3, data = munich_rent_index)
summary(additive_m4)
Call:
lm(formula = rentsqm ~ areainv + yearcc + yearcc2 + yearcc3,
data = munich_rent_index)
Residuals:
Min 1Q Median 3Q Max
-6.9920 -1.3705 -0.1354 1.3711 8.2834
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.11126 0.03674 193.575 <2e-16 ***
areainv 129.57166 5.53582 23.406 <2e-16 ***
yearcc 43.93838 2.07260 21.200 <2e-16 ***
yearcc2 27.53892 2.05958 13.371 <2e-16 ***
yearcc3 -1.75582 2.03998 -0.861 0.389
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.039 on 3077 degrees of freedom
Multiple R-squared: 0.3, Adjusted R-squared: 0.2991
F-statistic: 329.7 on 4 and 3077 DF, p-value: < 2.2e-16
Now we calculate the partial residuals for \(\text{area}\) and \(\text{yearc}\) define by:
\[
\hat{\epsilon}_{\text{area}, i} = \text{rentsqm}_{i} - \hat{\beta_{0}} -
\hat{f}_{2}(\text{yearc}_{i})
\]
With the effect \(f(\text{yearc}_{i})\):
\[
\hat{f}_{2}(\text{yearc}) = \beta_{2} \cdot \text{yearc}_{i} + \beta_{3}
\cdot {\text{yearc}_{i}}^{2} + \beta_{4} \cdot {\text{yearc}_{i}}^{3}
\]
And:
\[
\hat{\epsilon}_{\text{yearc}, i} = \text{rentsqm}_{i} - \hat{\beta_{0}}
- \hat{f}_{1}(\text{area}_{i})
\]
With the effect \(f(\text{yearc}_{i})\):
\[
\hat{f}_{1}(\text{yearc}) = \beta_{1} \cdot \frac{1}{\text{area}_{i}}
\]
par(mfrow = c(1, 2))
beta_add_m4 <- additive_m4$coefficients
area_seq <- seq(min(munich_rent_index$area), max(munich_rent_index$area), length.out = 100)
area_effect <- function(area){
inv_area <- 1 / area
cent_area <- inv_area - mean(inv_area)
beta_add_m4[2] * cent_area
}
yearc_effect <- function(yearc){
poly_yearc <- predict(poly_year, yearc - 1900)
beta_add_m4[3] * poly_yearc[, 1] + beta_add_m4[4] * poly_yearc[, 2] + beta_add_m4[5] * poly_yearc[, 3]
}
residual_area <- munich_rent_index$rentsqm - beta_add_m4[1] - yearc_effect(munich_rent_index$yearc)
plot(
munich_rent_index$area,
residual_area,
main = 'effect of area',
xlab = 'area in sqm',
ylab = 'effect of area / partial residuals'
)
lines(area_seq, area_effect(area_seq), col = 'red', lwd = 2)
residual_year <- munich_rent_index$rentsqm - beta_add_m4[1] - area_effect(munich_rent_index$area)
plot(
munich_rent_index$yearc,
residual_year,
main = 'effect of year of construction',
xlab = 'year of construction',
ylab = 'effect of year of construction / partial residuals'
)
lines(yearc_seq, yearc_effect(yearc_seq), col = 'red', lwd = 2)

Example 3.6 Munich Rent Index — Effect Coding
We adjust the regression model with the coding values of
location:
\[
\mathbb{E}(\text{rentsqm}_{i}) = \beta_{0} + \beta_{1} \cdot
\text{glocation}_{1} + \beta_{2} \cdot \text{tlocation}_{1}
\]
munich_rent_index$glocation <- 0
munich_rent_index$glocation[munich_rent_index$location == 2] <- 1
munich_rent_index$glocation[munich_rent_index$location == 1] <- -1
munich_rent_index$tlocation <- 0
munich_rent_index$tlocation[munich_rent_index$location == 3] <- 1
munich_rent_index$tlocation[munich_rent_index$location == 1] <- -1
cod_model <- lm(
rentsqm ~ glocation + tlocation,
data = munich_rent_index
)
summary(cod_model)
Call:
lm(formula = rentsqm ~ glocation + tlocation, data = munich_rent_index)
Residuals:
Min 1Q Median 3Q Max
-6.8564 -1.8376 -0.1074 1.7157 10.4494
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.46704 0.09638 77.477 < 2e-16 ***
glocation -0.19479 0.10445 -1.865 0.062285 .
tlocation 0.70529 0.18558 3.800 0.000147 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.426 on 3079 degrees of freedom
Multiple R-squared: 0.008867, Adjusted R-squared: 0.008223
F-statistic: 13.77 on 2 and 3079 DF, p-value: 1.109e-06
Example 3.7 Munich Rent Index — Interaction with Quality of
Kitchen
We import the new updated model of 2001, available at official page of the
dataset as all the datasets:
munich_rent_url_01 <- "https://www.uni-goettingen.de/de/document/download/e00d039e9c1f28ab404b27b0b14931f1.raw/rent98_00.raw"
munich_rent_index_01 <- read.table(
url(munich_rent_url_01),
header = 1,
colClasses = c(
"numeric", "numeric", "numeric",
"numeric", "integer", "integer",
"integer", "integer", "integer",
"integer", "numeric", "numeric",
"numeric"
)
)
summary(munich_rent_index_01)
rent_euro rentsqm_euro area yearc
Min. : 12.83 Min. : 0.1841 Min. : 20.00 Min. :1918
1st Qu.: 320.86 1st Qu.: 5.2601 1st Qu.: 52.00 1st Qu.:1939
Median : 424.12 Median : 6.8614 Median : 66.00 Median :1960
Mean : 456.94 Mean : 7.0274 Mean : 67.92 Mean :1956
3rd Qu.: 552.40 3rd Qu.: 8.7292 3rd Qu.: 82.00 3rd Qu.:1972
Max. :1837.89 Max. :17.6688 Max. :243.00 Max. :1999
glocation tlocation nkitchen pkitchen
Min. :0.0000 Min. :0.00000 Min. :0.00000 Min. :0.00000
1st Qu.:0.0000 1st Qu.:0.00000 1st Qu.:0.00000 1st Qu.:0.00000
Median :0.0000 Median :0.00000 Median :0.00000 Median :0.00000
Mean :0.3907 Mean :0.02516 Mean :0.09888 Mean :0.04004
3rd Qu.:1.0000 3rd Qu.:0.00000 3rd Qu.:0.00000 3rd Qu.:0.00000
Max. :1.0000 Max. :1.00000 Max. :1.00000 Max. :1.00000
eboden year01 yearc2 yearc3
Min. :0.0000 Min. :0.0000 Min. :3678724 Min. :7.060e+09
1st Qu.:0.0000 1st Qu.:0.0000 1st Qu.:3759721 1st Qu.:7.290e+09
Median :0.0000 Median :0.0000 Median :3841600 Median :7.530e+09
Mean :0.2986 Mean :0.3257 Mean :3828362 Mean :7.493e+09
3rd Qu.:1.0000 3rd Qu.:1.0000 3rd Qu.:3888784 3rd Qu.:7.670e+09
Max. :1.0000 Max. :1.0000 Max. :3996001 Max. :7.990e+09
invarea
Min. :0.004115
1st Qu.:0.012195
Median :0.015152
Mean :0.016904
3rd Qu.:0.019231
Max. :0.050000
And we adjust the model:
\[
\begin{matrix}
\widehat{\text{rentsqm}}_{i} = & \beta_{0} + \beta_{1}
\text{areainvc}_{i} + \beta_{2} \text{yearco}_{i} + \beta_{3}
\text{yearco2}_{i} + \beta_{4} \text{yearco3}_{i} \\
& + \beta_{5} \text{year01}_{i} + \beta_{6} \text{nkitchen}_{i} +
\beta_{7} \text{pkitchen}_{i} \\
& + \beta_{8} \text{nkitchen}_{i} \cdot \text{year01}_{i} +
\beta_{9} \text{pkitchen}_{i} \cdot \text{year01}_{i}
\end{matrix}
\]
munich_rent_index_01$areainvc <- munich_rent_index_01$invarea - mean(munich_rent_index_01$invarea)
munich_rent_index_01$yearco <- munich_rent_index_01$yearc - mean(munich_rent_index_01$yearc)
poly_year_01 <- poly(munich_rent_index_01$yearco, 3)
munich_rent_index_01$yearco <- poly_year_01[, 1]
munich_rent_index_01$yearco2 <- poly_year_01[, 2]
munich_rent_index_01$yearco3 <- poly_year_01[, 3]
interaction_model <- lm(
rentsqm_euro ~ areainvc + yearco + yearco2 + yearco3 + year01 + nkitchen + pkitchen + nkitchen * year01 + pkitchen * year01,
data = munich_rent_index_01
)
summary(interaction_model)
Call:
lm(formula = rentsqm_euro ~ areainvc + yearco + yearco2 + yearco3 +
year01 + nkitchen + pkitchen + nkitchen * year01 + pkitchen *
year01, data = munich_rent_index_01)
Residuals:
Min 1Q Median 3Q Max
-7.6303 -1.2647 -0.1072 1.2414 10.5233
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 6.95424 0.03841 181.072 < 2e-16 ***
areainvc 123.71308 4.42630 27.950 < 2e-16 ***
yearco 49.42282 2.02589 24.396 < 2e-16 ***
yearco2 29.45781 2.02386 14.555 < 2e-16 ***
yearco3 -1.03886 1.97176 -0.527 0.598311
year01 -0.25174 0.06678 -3.770 0.000166 ***
nkitchen 0.91567 0.12172 7.523 6.43e-14 ***
pkitchen 1.09081 0.17849 6.111 1.07e-09 ***
year01:nkitchen 0.39826 0.20922 1.904 0.057033 .
year01:pkitchen 0.73511 0.32864 2.237 0.025346 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.963 on 4561 degrees of freedom
Multiple R-squared: 0.3397, Adjusted R-squared: 0.3384
F-statistic: 260.7 on 9 and 4561 DF, p-value: < 2.2e-16
Example 3.8 Munich Rent Index — Interaction Between Living Area and
Location
The model to adjust ius defined as:
\[
\mathbb{E}(\text{rentsqm}_{i}) = \beta_{0} + \beta_{1} \cdot
\text{tlocation} + \beta_{2} \cdot \overline{\frac{1}{\text{area}}} +
\beta{3} \cdot \text{area} \cdot \text{tlocation}
\]
We use the dataset for average or top location only and change the
dummy variable \(\text{tlocation}\) to
\(0\) (average) and \(1\) (top). Finally, we visualize the effect
as described in the book.
par(mfrow = c(1, 2))
at_rent <- subset(munich_rent_index, location == 1 | location == 3)
at_rent$tlocation[at_rent$location == 1] <- 0
summary(at_rent)
rent rentsqm area yearc location
Min. : 54.92 Min. : 1.417 Min. : 20.00 Min. :1918 1:1794
1st Qu.: 314.62 1st Qu.: 5.276 1st Qu.: 50.00 1st Qu.:1954 2: 0
Median : 418.00 Median : 6.849 Median : 65.00 Median :1962 3: 78
Mean : 444.22 Mean : 7.007 Mean : 66.02 Mean :1959
3rd Qu.: 537.42 3rd Qu.: 8.652 3rd Qu.: 80.00 3rd Qu.:1972
Max. :1459.23 Max. :15.428 Max. :155.00 Max. :1997
bath kitchen cheating district yearco
Mode :logical Mode :logical Mode :logical 623 : 53 Min. :18.00
FALSE:1761 FALSE:1785 FALSE:148 1013 : 37 1st Qu.:54.00
TRUE :111 TRUE :87 TRUE :1724 713 : 32 Median :62.00
1132 : 31 Mean :59.36
1712 : 30 3rd Qu.:72.00
2024 : 30 Max. :97.00
(Other):1659
areainv yearcc yearcc2
Min. :-0.0105206 Min. :-0.030935 Min. :-0.0183373
1st Qu.:-0.0044722 1st Qu.:-0.001862 1st Qu.:-0.0147283
Median :-0.0015876 Median : 0.004598 Median :-0.0067317
Mean : 0.0002152 Mean : 0.002465 Mean :-0.0008428
3rd Qu.: 0.0030278 3rd Qu.: 0.012674 3rd Qu.: 0.0116794
Max. : 0.0330278 Max. : 0.032863 Max. : 0.0537875
yearcc3 glocation tlocation
Min. :-0.0186291 Min. :-1.0000 Min. :0.00000
1st Qu.:-0.0172309 1st Qu.:-1.0000 1st Qu.:0.00000
Median :-0.0056305 Median :-1.0000 Median :0.00000
Mean :-0.0004071 Mean :-0.9583 Mean :0.04167
3rd Qu.: 0.0096124 3rd Qu.:-1.0000 3rd Qu.:0.00000
Max. : 0.0588009 Max. : 0.0000 Max. :1.00000
area_location_model <- lm(
rentsqm ~ tlocation + areainv + area:tlocation,
data = at_rent
)
summary(area_location_model)
Call:
lm(formula = rentsqm ~ tlocation + areainv + area:tlocation,
data = at_rent)
Residuals:
Min 1Q Median 3Q Max
-7.2380 -1.4435 -0.1189 1.4788 7.1162
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 6.911e+00 4.967e-02 139.131 <2e-16 ***
tlocation 7.722e-01 7.280e-01 1.061 0.289
areainv 1.431e+02 7.434e+00 19.252 <2e-16 ***
tlocation:area 1.001e-02 8.612e-03 1.163 0.245
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 2.101 on 1868 degrees of freedom
Multiple R-squared: 0.1775, Adjusted R-squared: 0.1762
F-statistic: 134.4 on 3 and 1868 DF, p-value: < 2.2e-16
beta_al <- area_location_model$coefficients
f1_area <- function(area){
areainvc <- (1 / area) - mean(1 / area)
beta_al[3] * areainvc
}
f2_area <- function(area){
beta_al[4] * area
}
small_effect <- f1_area(area_seq)
big_effect <- beta_al[2] + f1_area(area_seq) + f2_area(area_seq)
ylim <- c(min(c(small_effect, big_effect)), max(c(small_effect, big_effect)))
plot(
area_seq,
small_effect,
main = 'area effects based on a linear interaction',
xlab = 'area in sqm',
ylab = 'area effects',
ylim = ylim,
type = 'l',
col = 'darkblue',
lwd = 2
)
lines(area_seq, big_effect, col = 'red', lwd = 2)
plot(
area_seq,
beta_al[2] + f2_area(area_seq),
main = 'varying effect of top location',
xlab = 'area in sqm',
ylab = 'Varying effect of top location',
type = 'l',
col = 'darkblue',
lwd = 2
)

Example 3.9 Orthogonal Design
Just for exemplify, we define the orthogonal process describe as an R
function:
\[
\tilde{x}^{j} = x^{j} - \widetilde{X}_{j} \left (
{\widetilde{X}_{j}}^{T} \widetilde{X}_{j} \right )^{-1}
{\widetilde{X}_{j}}^{T} x^{j}
\]
orthogonal_design <- function(x){
x_out <- cbind(x[, 1] - mean(x[, 1]), matrix(0, nrow = nrow(x), ncol = ncol(x) - 1))
for (i in 2:ncol(x)){
x_hat <- as.matrix(x_out[, 1:(i-1)])
x_inv <- solve(t(x_hat) %*% x_hat)
x_out[, i] <- x[, i] - x_hat %*% x_inv %*% t(x_hat) %*% x[, i]
}
x_out
}
We test this function on the polynomial of the \(\text{yearc}\) variable:
poly_test <- poly(munich_rent_index$yearc, 3, raw = TRUE)
orthogonal_test <- orthogonal_design(poly_test)
# And check it
c(
orthogonal_test[, 1] %*% orthogonal_test[, 2],
orthogonal_test[, 2] %*% orthogonal_test[, 3],
orthogonal_test[, 1] %*% orthogonal_test[, 3]
)
[1] 7.550556e-05 8.482947e+05 2.602290e+00
Why did it not result?
Example 3.10 Munich Rent Index — Comparison of Models Using \({R}^{2}\)
We compare the models: \(M1\), \(M2\), \(M3\), and \(M4\) using its coefficient of determination
\(R^{2}\)
coeff_deter <- data.frame(
`R Squared` = c(summary(m1)$r.squared, summary(m2)$r.squared, summary(m3)$r.squared, summary(m4)$r.squared),
row.names = c("M1", "M2", "M3", "M4")
)
coeff_deter
Example 3.11 Simple Linear Regression
For the model:
\[
y_{i} = \beta x_{i} + \epsilon_{i}
\]
We can verify:
\[
\left ( {X_{n}}^{T} X_{n} \right )^{-1} \to 0
\]
And:
\[
\max_{i=1,...,n} {x_{i}}^{T} \left ( {X_{n}}^{T} X_{n} \right )^{-1}
x_{i} \to 0 \text{ for } n \to \infty
\]
\(x_{i} = i\):
\[
\left ( {X_{n}}^{T} X_{n} \right )^{-1} = \left ( (1, 2, 3, \cdots, n)
\left ( \begin{matrix}
1 \\
2 \\
\vdots \\
n
\end{matrix} \right ) \right )^{-1} = \left ( 1 + 4 + \cdots + n^2
\right )^{-1} \to 0
\]
And:
\[
\max_{i=1,...,n} ( 1, 2, \cdots, n ) \left ( 1 + 4 + \cdots + n^2 \right
)^{-1} \left ( \begin{matrix}
1 \\
2 \\
\vdots \\
n
\end{matrix} \right ) \to 0 \text{ for } n \to \infty
\]
\(x_{i} = \frac{1}{i}\):
\[
\left ( {X_{n}}^{T} X_{n} \right )^{-1} = \left ( \left (1, 0.5, 0.33,
\cdots, \frac{1}{n} \right ) \left ( \begin{matrix}
1 \\
0.5 \\
\vdots \\
\frac{1}{n}
\end{matrix} \right ) \right )^{-1} = \left ( \sum_{i=1}^{n}
\frac{1}{i^{2}} \right )^{-1} \not{\to} 0
\]
And:
\[
\max_{i=1,...,n} \left ( 1, 0.5, \cdots, \frac{1}{n} \right ) \left (
\sum_{i = 1}^{n} \frac{1}{i^{2}} \right )^{-1} \left ( \begin{matrix}
1 \\
0.5 \\
\vdots \\
\frac{1}{n}
\end{matrix} \right ) \not{\to} 0 \text{ for } n \to \infty
\]
\(x_{i} =
\frac{1}{\sqrt{i}}\):
\[
\left ( {X_{n}}^{T} X_{n} \right )^{-1} = \left ( \sum_{i = 1}^{n}
\frac{1}{i} \right )^{-1} \to 0
\]
And:
\[
\max_{i=1,...,n} \left ( 1, \frac{1}{\sqrt{2}}, \cdots,
\frac{1}{\sqrt{n}} \right ) \left ( \sum_{i = 1}^{n} \frac{1}{i} \right
)^{-1} \begin{matrix}
1 \\
\frac{1}{\sqrt{2}} \\
\vdots \\
\frac{1}{\sqrt{n}}
\end{matrix} \to 0
\]
Example 3.12 Munich Rent Index — Hypothesis Testing
The regression model to adjust is:
\[
\begin{matrix}
\text{rentsqm}_{i} = & \beta_{0} + \beta_{1} \text{areainvc}_{i} +
\beta_{2} \text{yearco}_{i} + \beta_{3} \text{yearco2}_{i} + \beta_{4}
\text{yearco3}_{i} \\
& + \beta_{5} \text{nkitchen}_{i} + \beta_{6} \text{pkitchen}_{i} +
\beta_{7} \text{year01}_{i} + \epsilon_{i}
\end{matrix}
\]
hypothesis_model <- lm(
rentsqm_euro ~ areainvc + yearco + yearco2 + yearco3 + nkitchen + pkitchen + year01,
data = munich_rent_index_01
)
summary(hypothesis_model)
Call:
lm(formula = rentsqm_euro ~ areainvc + yearco + yearco2 + yearco3 +
nkitchen + pkitchen + year01, data = munich_rent_index_01)
Residuals:
Min 1Q Median 3Q Max
-7.674 -1.270 -0.113 1.242 10.479
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 6.93239 0.03757 184.539 < 2e-16 ***
areainvc 123.76987 4.42908 27.945 < 2e-16 ***
yearco 49.37274 2.02573 24.373 < 2e-16 ***
yearco2 29.49985 2.02515 14.567 < 2e-16 ***
yearco3 -0.88418 1.97229 -0.448 0.65396
nkitchen 1.04340 0.10151 10.279 < 2e-16 ***
pkitchen 1.30205 0.15226 8.552 < 2e-16 ***
year01 -0.18524 0.06213 -2.981 0.00288 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 1.964 on 4563 degrees of freedom
Multiple R-squared: 0.3385, Adjusted R-squared: 0.3375
F-statistic: 333.6 on 7 and 4563 DF, p-value: < 2.2e-16
So we want to test the hypothesis:
\[
\begin{matrix}
H_{0} : \beta_{7} = 0 & \text{against} & H_{1} : \beta_{7} \neq
0
\end{matrix}
\]
About the significance of the follow-up measure.
\[
\begin{matrix}
H_{0} : \left ( \begin{matrix}
\beta_{5} \\
\beta_{6}
\end{matrix} \right ) = \left ( \begin{matrix}
0 \\
0
\end{matrix} \right ) & \text{against} & H_{1} : \left (
\begin{matrix}
\beta_{5} \\
\beta_{6}
\end{matrix} \right ) \neq \left ( \begin{matrix}
0 \\
0
\end{matrix} \right )
\end{matrix}
\]
About the significance of the kitchen variable. And a final test
about the significance of differentiate between these two type of
kitchens:
\[
\begin{matrix}
H_{0} : \beta_{5} - \beta_{6} = 0 & \text{against} & H_{1} :
\beta_{5} - \beta_{6} \neq 0
\end{matrix}
\]
Example 3.13 Munich Rent Index — Standard Output and Hypothesis
Tests
For the first test (\(H_{0} : \beta_{7} =
0\)), we calculate:
\[
t_{7} =
\frac{\hat{\beta}_{7}}{{\widehat{\text{Var}(\hat{\beta}_{7})}}^{1/2}}
\sim t_{1 - \alpha / 2} (n - p)
\]
The covariance matrix is:
cov_matrix <- vcov(hypothesis_model)
cov_matrix
(Intercept) areainvc yearco yearco2 yearco3
(Intercept) 0.001411211 0.006823735 0.004991278 0.005173670 -0.002333528
areainvc 0.006823735 19.616785356 -1.294964483 1.420604414 0.122987111
yearco 0.004991278 -1.294964483 4.103591466 0.046451741 -0.006404590
yearco2 0.005173670 1.420604414 0.046451741 4.101229176 0.027782090
yearco3 -0.002333528 0.122987111 -0.006404590 0.027782090 3.889943976
nkitchen -0.001093652 -0.091944737 -0.025946481 -0.026748293 0.006345756
pkitchen -0.001128930 0.022989833 -0.042249319 -0.049085309 -0.016014007
year01 -0.001270640 0.004137400 -0.002253660 -0.001730023 0.007205398
nkitchen pkitchen year01
(Intercept) -1.093652e-03 -0.0011289298 -1.270640e-03
areainvc -9.194474e-02 0.0229898326 4.137400e-03
yearco -2.594648e-02 -0.0422493193 -2.253660e-03
yearco2 -2.674829e-02 -0.0490853093 -1.730023e-03
yearco3 6.345756e-03 -0.0160140070 7.205398e-03
nkitchen 1.030419e-02 0.0014042193 5.682974e-05
pkitchen 1.404219e-03 0.0231824417 1.902243e-04
year01 5.682974e-05 0.0001902243 3.860040e-03
We check the \(\text{Var}(\hat{\beta}_{7})\) is the last
value.
var_7 <- cov_matrix[8, 8]
t_7 <- hypothesis_model$coefficients[8] / sqrt(var_7)
t_7
year01
-2.981496
And this coincides with the value given in the
summary.
For the second test, we compute the \(F\) value as:
\[
F = \frac{1}{r} {\hat{\beta}}^{T} \widehat{\text{Cov} \left (
\hat{\beta} \right )}^{-1} \hat{\beta} \sim F_{1, n - p}
\]
With the \(\hat{\beta}\) define in
the test \(\left ( \begin{matrix}
\hat{\beta_{5}} \\
\hat{\beta_{6}}
\end{matrix} \right )\), then \(r=2\) and \(n - p
= n - 8\):
df <- nrow(munich_rent_index_01) - 8
r <- 2
beta_hat <- as.matrix(hypothesis_model$coefficients[6:7])
f_56 <- (1/2) * t(beta_hat) %*% solve(cov_matrix[6:7, 6:7]) %*% beta_hat
f_56
[,1]
[1,] 82.08307
We get expected \(F\):
f_crit <- qf(p = 0.95, r, df)
f_crit
[1] 2.9977
And we reject the null hypothesis.
The third and final test comparing \(\beta_{5}\) and \(\beta_{6}\) between each other. We
need:
\[
\widehat{\text{Var}(\hat{\beta}_{5} - \hat{\beta}_{6})} =
\widehat{\text{Var}(\hat{\beta}_{5})} +
\widehat{\text{Var}(\hat{\beta}_{6})} - 2 \cdot
\widehat{\text{Cov}(\hat{\beta}_{5}, \hat{\beta}_{6})}
\]
Using the \(\widehat{\text{Cov}(\hat{\beta})}\)
matrix:
cov_5_6 <- cov_matrix[6, 7]
var_5 <- cov_matrix[6, 6]
var_6 <- cov_matrix[7, 7]
var_5_6 <- var_5 + var_6 - 2 * cov_5_6
f_5_6 <- (hypothesis_model$coefficients[6] - hypothesis_model$coefficients[7])^2 / var_5_6
f_5_6
nkitchen
2.18071
And this \(F\) critical, using \(r=1\):
f_crit <- qf(p = 0.95, 1, df)
f_crit
[1] 3.843498
So, in this case, we do not reject the null hypothesis.
Example 3.14 Munich Rent Index — Confidence Intervals
The confidence interval for \(\beta_{7}\) is obtain using:
\[
\beta_{7} \pm t_{n - p} \left (1 - \alpha / 2 \right ) \cdot
\widehat{\text{Var}(\hat{\beta}_{7})}^{1/2}
\]
inter <- qt(1 - 0.05 / 2, df) * sqrt(var_7)
c(hypothesis_model$coefficients[8] - inter, hypothesis_model$coefficients[8] + inter)
year01 year01
-0.3070414 -0.0634347
Example 3.15 Munich Rent Index — Prediction Intervals
Using the prediction interval:
\[
{x_{0}}^{T} \cdot \hat{\beta} \pm t_{n-p}(1 - \alpha / 2) \hat{\sigma}
\left ( 1 + {x_{0}}^{T} \left (X^{T} X \right )^{1} x_{0} \right )^{1/2}
\]
We can obtain the \(\hat{\sigma}\)
directly from the model or using:
\[
(n - p) \hat{\sigma}^2 = \epsilon^{T} \epsilon
\]
sigma_model <- summary(hypothesis_model)$sigma
sigma_2 <- t(as.matrix(hypothesis_model$residuals)) %*% as.matrix(hypothesis_model$residuals) / (nrow(munich_rent_index_01) - 8)
sigma <- sqrt(sigma_2)
c(sigma_model, sigma)
[1] 1.964112 1.964112
We are also going to use the design matrix:
design_matrix <- as.matrix(hypothesis_model$model)
design_matrix[, 1] <- 1
colnames(design_matrix)[1] <- "1"
head(design_matrix)
1 areainvc yearco yearco2 yearco3 nkitchen pkitchen
1 1 0.0116672025 -0.011568466 -0.010460264 0.030118199 0 0
2 1 -0.0072887975 -0.011568466 -0.010460264 0.030118199 0 0
3 1 0.0175786025 0.009594692 -0.004320479 -0.013940551 0 0
4 1 0.0087368025 0.010256041 -0.003177207 -0.014569233 0 0
5 1 -0.0065948975 0.018853574 0.016932467 -0.005009151 0 0
6 1 -0.0007751975 0.003642554 -0.012015191 -0.002877678 0 0
year01
1 0
2 0
3 0
4 0
5 0
6 0
area_seq <- seq(min(munich_rent_index_01$area), max(munich_rent_index_01$area), length.out = 100)
areainvc <- (1 / area_seq) - mean(1 / area_seq)
betam <- hypothesis_model$coefficients
yearcc <- munich_rent_index_01[munich_rent_index_01$yearc == 1918, ][1, ]
# Three last terms were added for completeness
constant <- betam[1] + betam[3] * yearcc$yearco + betam[4] * yearcc$yearco2 + betam[5] * yearcc$yearco3 + betam[6] * 0 + betam[7] * 0 + betam[8] * 0
plot(
munich_rent_index_01$area,
munich_rent_index_01$rentsqm_euro,
xlab = 'area in sqm',
ylab = 'estimated rent per sqm'
)
beta_1 <- hypothesis_model$coefficients[2]
lines(area_seq, constant + beta_1 * areainvc, col = 'red', lwd = 2)
conf_inter <- qt(1 - 0.05 / 2, df) * sqrt(cov_matrix[2, 2])
beta_conf <- c(beta_1 - conf_inter, beta_1 + conf_inter)
lines(area_seq, constant + beta_conf[1] * areainvc, col = 'darkblue', lwd = 2)
lines(area_seq, constant + beta_conf[2] * areainvc, col = 'darkblue', lwd = 2)
x_o <- as.matrix(cbind(
1,
areainvc,
yearcc$yearco,
yearcc$yearco2,
yearcc$yearco3,
0,
0,
0
))
pred_inter <- NULL
for (i in seq_len(nrow(x_o)))
x_o_i <- as.matrix(x_o[i, ])
pred_inter <- c(pred_inter, qt(1 - 0.05 / 2, df) * sigma * sqrt(
1 + (
t(x_o_i) %*% solve(t(design_matrix)%*%design_matrix) %*% x_o_i
))
)
lines(area_seq, constant + beta_conf[1] * areainvc - pred_inter, col = 'gray', lwd = 2)
lines(area_seq, constant + beta_conf[2] * areainvc + pred_inter, col = 'gray', lwd = 2)

This can also be done using predict:
x_o <- as.data.frame(cbind(
areainvc,
yearcc$yearco,
yearcc$yearco2,
yearcc$yearco3,
0,
0,
0
))
colnames(x_o) <- colnames(hypothesis_model$model)[2:8]
confidence_interval <- predict(hypothesis_model, x_o, interval = "confidence")
plot(
munich_rent_index_01$area,
munich_rent_index_01$rentsqm_euro,
xlab = 'area in sqm',
ylab = 'estimated rent per sqm'
)
lines(area_seq, confidence_interval[, "fit"], col = 'red', lwd = 2)
lines(area_seq, confidence_interval[, "lwr"], col = 'darkblue', lwd = 2)
lines(area_seq, confidence_interval[, "upr"], col = 'darkblue', lwd = 2)
prediction_interval <- predict(hypothesis_model, x_o, interval = "prediction")
lines(area_seq, prediction_interval[, "lwr"], col = 'gray', lwd = 2)
lines(area_seq, prediction_interval[, "upr"], col = 'gray', lwd = 2)

Example 3.16 Polynomial Regression
Simulated data from the real model:
\[
y_{i} = -1 + 0.3 x_{i} + 0.4 {x_{i}}^{2} - 0.8 {x_{i}}^{3} +
\epsilon_{i} \text{ with } \epsilon_{i} \sim N(0, {0.07}^{2})
\]
par(mfrow = c(3, 2))
x_seq <- seq(0, 1, length.out = 50)
training_data <- data.frame(
x = x_seq,
y = -1 + 0.3 * x_seq + 0.4 * x_seq ^ 2 - 0.8 * x_seq ^ 3 + rnorm(50, 0, 0.07)
)
validation_data <- data.frame(
x = x_seq,
y = -1 + 0.3 * x_seq + 0.4 * x_seq ^ 2 - 0.8 * x_seq ^ 3 + rnorm(50, 0, 0.07)
)
plot_training_data <- function(title) {
plot(
training_data,
main = title,
xlab = 'x',
ylab = 'y'
)
}
plot_training_data('training_data')
plot(
validation_data,
main = 'validation_data',
xlab = 'x',
ylab = 'y'
)
adjust_polynomial <- function(l) {
model <- "y ~ x"
if (l != 1) {
for (i in 2:l){
model <- paste0(model, " + I(x^", i, ")")
}
}
lm(model, data = training_data)
}
polynomial_models <- lapply(1:9, adjust_polynomial)
plot_training_data('regression line')
lines(x_seq, predict(polynomial_models[1][[1]]), col = 'red', lwd = 2)
plot_training_data('polynomial regression with l=2')
lines(x_seq, predict(polynomial_models[2][[1]]), col = 'red', lwd = 2)
plot_training_data('polynomial regression with l=5')
lines(x_seq, predict(polynomial_models[5][[1]]), col = 'red', lwd = 2)
mean_square_error <- function(model, data){
(1 / 50) * sum((data$y - predict(model, new_data = data)) ^ 2)
}
mse_training_data <- unlist(lapply(polynomial_models, mean_square_error, data = training_data))
mse_validation_data <- unlist(lapply(polynomial_models, mean_square_error, data = validation_data))
plot(
seq(1, 9, 1),
mse_training_data,
ylim = c(min(mse_training_data, mse_validation_data), max(mse_training_data, mse_validation_data)),
main = 'MSE for training and validation data',
xlab = 'degree of polynomial',
ylab = 'MSE',
type = 'l',
col = 'darkblue',
lwd = 2
)
lines(seq(1, 9, 1), mse_validation_data, col = 'red', lwd = 2)

Example 3.17 Correlated Covariates
Simulate the data as following:
\[
x_{1} \sim U(0, 1) \wedge x_{3} \sim U(0, 1)
\]
\[
x_{2} = x_{1} + u \text{ with } u \sim U(0, 1)
\]
\[
y \sim N(-1 + 0.3 x_{1} + 0.2 x_{3}, {0.2}^{2})
\]
n <- 150
x1 <- runif(n, 0, 1)
x3 <- runif(n, 0, 1)
u <- runif(n, 0, 1)
x2 <- x1 + u
y <- rnorm(n, mean = -1 + 0.3 * x1 + 0.2 * x3, 0.2)
corr_cov_data <- data.frame(
y = y,
x1 = x1,
x2 = x2,
x3 = x3
)
pairs(corr_cov_data)

Adjust the model with \(x_{1}\),
\(x_{2}\) and \(x_{3}\):
wrong_model <- lm(y ~ x1 + x2 + x3, data = corr_cov_data)
summary(wrong_model)
Call:
lm(formula = y ~ x1 + x2 + x3, data = corr_cov_data)
Residuals:
Min 1Q Median 3Q Max
-0.45604 -0.12357 0.00926 0.11418 0.51568
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.983404 0.049308 -19.944 < 2e-16 ***
x1 0.308749 0.077842 3.966 0.000114 ***
x2 -0.003877 0.054825 -0.071 0.943728
x3 0.138695 0.051768 2.679 0.008228 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.1858 on 146 degrees of freedom
Multiple R-squared: 0.2057, Adjusted R-squared: 0.1894
F-statistic: 12.6 on 3 and 146 DF, p-value: 2.252e-07
Adjust the model with \(x_{1}\) and
\(x_{3}\):
correct_model <- lm(y ~ x1 + x3, data = corr_cov_data)
summary(correct_model)
Call:
lm(formula = y ~ x1 + x3, data = corr_cov_data)
Residuals:
Min 1Q Median 3Q Max
-0.45722 -0.12438 0.00888 0.11348 0.51427
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.98526 0.04160 -23.685 < 2e-16 ***
x1 0.30469 0.05242 5.812 3.69e-08 ***
x3 0.13868 0.05159 2.688 0.00802 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 0.1852 on 147 degrees of freedom
Multiple R-squared: 0.2057, Adjusted R-squared: 0.1949
F-statistic: 19.03 on 2 and 147 DF, p-value: 4.465e-08
Example 3.18 Polynomial Regression — Model Choice with AIC
We calculate AIC using:
\[
AIC = n \cdot \log(\hat{\sigma}^{2}) + 2 \left ( \left | M \right | + 1
\right )
\]
aic <- function(model){
m <- ncol(model$model) - 1
n <- nrow(model$model)
n * log(summary(model)$sigma^2) + 2 * (m + 1)
}
aic_values <- unlist(lapply(polynomial_models, aic))
plot(
aic_values,
main = 'AIC as a function of degree of polynomials',
xlab = 'degrees of polynomial',
ylab = 'aic',
type = 'l',
col = 'darkblue',
lwd = 2
)

As always, this can also be done using R:
aic <- function(model){
AIC(model)
}
aic_values <- unlist(lapply(polynomial_models, aic))
plot(
aic_values,
main = 'AIC as a function of degree of polynomials',
xlab = 'degrees of polynomial',
ylab = 'aic',
type = 'l',
col = 'darkblue',
lwd = 2
)

Example 3.19 Prices of Used Cars — Model Choice
As for all dataset, we import the “prices of used cars”
dataset from the official page of the
dataset:
cars_url <- "https://www.uni-goettingen.de/de/document/download/062cadfda1c4c295f9460b49c7f5799e.raw/golffull.raw"
used_cars <- read.table(
url(cars_url),
header = 1,
colClasses = c(
"numeric", "integer", "numeric",
"integer", "factor", "factor",
"numeric", "numeric", "numeric",
"numeric", "numeric", "numeric"
)
)
# Convert to logical
used_cars$extras1 <- used_cars$extras1 == 1
used_cars$extras2 <- used_cars$extras2 == 1
# Recalculate polynomials
poly_kilometer <- poly(used_cars$kilometer, 3)
used_cars[, c("kilometerop1", "kilometerop2", "kilometerop3")] <- poly_kilometer
poly_age <- poly(used_cars$age, 3)
used_cars[, c("ageop1", "ageop2", "ageop3")] <- poly_age
summary(used_cars)
price age kilometer TIA
Min. :1.450 Min. : 65.00 Min. : 10.0 Min. : 0.00
1st Qu.:2.450 1st Qu.: 99.75 1st Qu.:107.0 1st Qu.:11.00
Median :3.100 Median :115.00 Median :130.8 Median :19.00
Mean :3.397 Mean :113.19 Mean :134.7 Mean :16.98
3rd Qu.:3.960 3rd Qu.:129.00 3rd Qu.:167.5 3rd Qu.:24.00
Max. :7.300 Max. :142.00 Max. :250.0 Max. :26.00
extras1 extras2 kilometerop1 kilometerop2
Mode :logical Mode :logical Min. :-0.213325 Min. :-0.05651
FALSE:54 FALSE:43 1st Qu.:-0.047433 1st Qu.:-0.04987
TRUE :118 TRUE :129 Median :-0.006815 Median :-0.03005
Mean : 0.000000 Mean : 0.00000
3rd Qu.: 0.056036 3rd Qu.: 0.02175
Max. : 0.197130 Max. : 0.40437
kilometerop3 ageop1 ageop2 ageop3
Min. :-0.532904 Min. :-0.193852 Min. :-0.07929 Min. :-0.43373
1st Qu.:-0.047845 1st Qu.:-0.054070 1st Qu.:-0.06335 1st Qu.:-0.05421
Median : 0.009036 Median : 0.007273 Median :-0.02235 Median :-0.01263
Mean : 0.000000 Mean : 0.000000 Mean : 0.00000 Mean : 0.00000
3rd Qu.: 0.045786 3rd Qu.: 0.063588 3rd Qu.: 0.04385 3rd Qu.: 0.06266
Max. : 0.342683 Max. : 0.115881 Max. : 0.32072 Max. : 0.18010
Exploring the data:
par(mfrow = c(3, 2))
plot(
used_cars$age,
used_cars$price,
main = 'Scatter plot: sales price versus age in months',
xlab = 'age in months',
ylab = 'sales prince in 1000 Euro'
)
plot(
used_cars$kilometer,
used_cars$price,
main = 'Scatter plot: sales price versus kilometer reading',
xlab = 'kilometer reading in 1000 km',
ylab = 'sales prince in 1000 Euro'
)
plot(
used_cars$TIA,
used_cars$price,
main = 'Scatter plot: sales price versus months until TIA',
xlab = 'months until next TIA appointment',
ylab = 'sales prince in 1000 Euro'
)
boxplot(
price ~ extras1,
data = used_cars,
main = 'Box plot: sales prince with or without ABS',
xlab = '',
ylab = 'sales prices in 1000 Euro',
names = c('no ABS', 'ABS')
)
boxplot(
price ~ extras2,
data = used_cars,
main = 'Box plot: sales prince with or without sunroof',
xlab = '',
ylab = 'sales prices in 1000 Euro',
names = c('no sunroof', 'sunroof')
)

We explore the models suggested:
\(M1\)
\[
\begin{matrix}
\mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1}
\text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3}
\text{kilometerop3} \\
& + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6}
\text{ageop3}
\end{matrix}
\]
With \(\text{kilometerop}i\) the
\(i\) term of a polynomial of degree 3
of the \(\text{kilometer}\) variable.
Equivalent to \(\text{ageop}i\).
\(M2\)
\[
\begin{matrix}
\mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1}
\text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3}
\text{kilometerop3} \\
& + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6}
\text{ageop3} \\
& + \beta_{7} \text{extras1}
\end{matrix}
\]
\(M3\)
\[
\begin{matrix}
\mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1}
\text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3}
\text{kilometerop3} \\
& + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6}
\text{ageop3} \\
& + \beta_{7} \text{extras2}
\end{matrix}
\]
\(M4\)
\[
\begin{matrix}
\mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1}
\text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3}
\text{kilometerop3} \\
& + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6}
\text{ageop3} \\
& + \beta_{7} \text{TIA}
\end{matrix}
\]
\(M5\)
\[
\begin{matrix}
\mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1}
\text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3}
\text{kilometerop3} \\
& + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6}
\text{ageop3} \\
& + \beta_{7} \text{extras1} + \beta_{8} \text{extras2}
\end{matrix}
\]
\(M6\)
\[
\begin{matrix}
\mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1}
\text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3}
\text{kilometerop3} \\
& + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6}
\text{ageop3} \\
& + \beta_{7} \text{extras1} + \beta_{8} \text{TIA}
\end{matrix}
\]
\(M7\)
\[
\begin{matrix}
\mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1}
\text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3}
\text{kilometerop3} \\
& + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6}
\text{ageop3} \\
& + \beta_{7} \text{extras2} + \beta_{8} \text{TIA}
\end{matrix}
\]
\(M8\)
\[
\begin{matrix}
\mathbb{E}(\text{price}) = & \beta_{0} + \beta_{1}
\text{kilometerop1} + \beta_{2} \text{kilometerop2} + \beta_{3}
\text{kilometerop3} \\
& + \beta_{4} \text{ageop1} + \beta_{5} \text{ageop2} + \beta_{6}
\text{ageop3} \\
& + \beta_{7} \text{extras1} + \beta_{8} \text{extras2} +
\beta_{9} \text{TIA}
\end{matrix}
\]
base_car_model <- "price ~ kilometerop1 + kilometerop2 + kilometerop3 + ageop1 + ageop2 + ageop3"
used_cars_model <- list(lm(base_car_model, data = used_cars))
extra_var <- c("extras1", "extras2", "TIA")
for (extra in extra_var){
new_model <- paste0(base_car_model, " + ", extra)
used_cars_model[[length(used_cars_model) + 1]] <- lm(new_model, data = used_cars)
}
for (tuple in combn(extra_var, 2, simplify = FALSE)){
new_model <- paste0(base_car_model, " + ", tuple[1], " + ", tuple[2])
used_cars_model[[length(used_cars_model) + 1]] <- lm(new_model, data = used_cars)
}
all_extras <- paste(extra_var, collapse = " + ")
used_cars_model[[length(used_cars_model) + 1]] <- lm(paste0(base_car_model, " + ", all_extras), data = used_cars)
cars_aic <- data.frame(model = seq(1, 8), aic = unlist(lapply(used_cars_model, AIC)))
cars_aic
9th model are obtained using “leaps and bounds” algorithm,
already implemented on R:
library(leaps)
leaps_model <- regsubsets(price ~ . -age -kilometer, data = used_cars, nvmax = 4)
summary(leaps_model)
Subset selection object
Call: regsubsets.formula(price ~ . - age - kilometer, data = used_cars,
nvmax = 4)
9 Variables (and intercept)
Forced in Forced out
TIA FALSE FALSE
extras1TRUE FALSE FALSE
extras2TRUE FALSE FALSE
kilometerop1 FALSE FALSE
kilometerop2 FALSE FALSE
kilometerop3 FALSE FALSE
ageop1 FALSE FALSE
ageop2 FALSE FALSE
ageop3 FALSE FALSE
1 subsets of each size up to 4
Selection Algorithm: exhaustive
TIA extras1TRUE extras2TRUE kilometerop1 kilometerop2 kilometerop3
1 ( 1 ) " " " " " " " " " " " "
2 ( 1 ) " " " " " " "*" " " " "
3 ( 1 ) " " " " " " "*" " " " "
4 ( 1 ) " " " " " " "*" "*" " "
ageop1 ageop2 ageop3
1 ( 1 ) "*" " " " "
2 ( 1 ) "*" " " " "
3 ( 1 ) "*" "*" " "
4 ( 1 ) "*" "*" " "
*
*
*
*
*
*
*
*
*
*
As in the book the resulted model is:
\(M9\)
\[
\mathbb{E}(\text{price}) = \beta_{0} + \beta_{1} \text{kilometerop1} +
\beta_{2} \text{kilometerop2} + \beta_{3} \text{ageop1} + \beta_{4}
\text{ageop2}
\]
We add it to the table and plot the AIC:
selected_var <- names(which(summary(leaps_model)$outmat[4, ] == "*"))
used_cars_model[[length(used_cars_model) + 1 ]] <- lm(
paste0("price ~ ", paste(selected_var, collapse= " + ")), data = used_cars
)
cars_aic[9, ] <- c(9, AIC(used_cars_model[[9]]))
cars_aic
plot(
cars_aic,
main = 'AIC for the different models',
xlab = 'model number',
ylab = 'AIC',
xaxt = 'n'
)
axis(1, at = 1:9)

Showing the age and kilometer effects as in Example 3.5.
par(mfrow = c(1, 2))
beta_m9 <- used_cars_model[[9]]$coefficients
age_seq <- seq(min(used_cars$age), max(used_cars$age), length.out = 100)
kilometer_seq <- seq(min(used_cars$kilometer), max(used_cars$kilometer), length.out = 100)
age_effect <- function(age){
i_poly_age <- predict(poly_age, age)
beta_m9[4] * i_poly_age[, 1] + beta_m9[5] * i_poly_age[, 2]
}
kilometer_effect <- function(kilometer){
i_poly_killometer <- predict(poly_kilometer, kilometer)
beta_m9[2] * i_poly_killometer[, 1] + beta_m9[3] * i_poly_killometer[, 2]
}
residual_age <- used_cars$price - beta_m9[1] - kilometer_effect(used_cars$kilometer)
plot(
used_cars$age,
residual_age,
main = 'effect of age including partial residuals',
xlab = 'age in months',
ylab = 'effect / partial residuals'
)
lines(age_seq, age_effect(age_seq), col = 'red', lwd = 2)
residual_kilometer <- used_cars$price - beta_m9[1] - age_effect(used_cars$age)
plot(
used_cars$kilometer,
residual_age,
main = 'effect of kilometer reading including partial residuals',
xlab = 'kilometer reading in 1000km',
ylab = 'effect / partial residuals'
)
lines(kilometer_seq, kilometer_effect(kilometer_seq), col = 'red', lwd = 2)

Example 3.20 Price of Used Cars — Model Diagnosis
The studentized residuals are calculated with the equation:
\[
r_{i}^{*} = \frac{\hat{\epsilon}_{i}}{\hat{\sigma}_{(i)} \sqrt{1 -
h_{ii}}}
\]
But, you can get them using R:
par(mfrow = c(2, 2))
t_residuals <- rstudent(used_cars_model[[9]])
n <- nrow(used_cars_model[[9]]$model)
p <- ncol(used_cars_model[[9]]$model)
t_critical <- qt(1 - 0.05 / (2 * n), n - p - 1)
plot_studentized_residuals <- function(against, ag_label, short_label){
plot(
against,
t_residuals,
main = paste0('studentized residuals verus ', short_label),
xlab = ag_label,
ylab = 'studentized residuals',
ylim = c(min(c(-t_critical - 0.5, t_residuals)), max(c(t_critical + 0.5, t_residuals)))
)
abline(h = 0, col = 'red', lwd = 2)
abline(h = c(-t_critical, t_critical), col = 'gray', lwd = 2)
}
plot_studentized_residuals(used_cars_model[[9]]$fitted.values, 'estimated sales price', 'estimated sales price')
plot_studentized_residuals(used_cars$kilometer, 'kilometer reading in 1000km', 'kilometer reading')
plot_studentized_residuals(used_cars$age, 'age in months', 'age in months')
plot_studentized_residuals(used_cars$TIA, 'months to the next TIA appointment', 'months to TIA')

For the standardized residuals:
\[
r_{i} = \frac{\hat{\epsilon}_{i}}{\hat{\sigma}\sqrt{1 - h_{ii}}}
\]
But using R:
n_residuals <- rstandard(used_cars_model[[9]])
qqnorm(n_residuals, col = 'darkblue', main = '', xlab = 'quantiles of normal distribution', ylab = 'quantiles of residuals')
abline(0, 1, col = 'black', lwd = 2)

Example 3.21 Prices of Used Cars — Collinearity
Now, we want the \(VIF\):
\[
{VIF}_{j} = \frac{1}{1 - {R_{j}}^{2}}
\]
library(car)
Loading required package: carData
vif(used_cars_model[[9]])
kilometerop1 kilometerop2 ageop1 ageop2
1.179534 1.033982 1.187743 1.025773
All values \(< 10\).
Example 3.22 Prices of Used Cars—Outliers
See graph of example 3.20. Finding the outlier:
t_residuals[t_residuals > t_critical]
35
4.364175
Example 3.23 Prices of Used Cars — Influence Analysis
Getting the \(h_{ii}\) values:
\[
h_{ii} = \frac{1}{n} + {\left ( x_{i} - \overline{x} \right )}^{T} \left
( {\widetilde{X}_{j}}^{T} \widetilde{X}_{j} \right )^{-1} \left ( x_{i}
- \overline{x} \right )
\]
leverage <- hatvalues(used_cars_model[[9]])
head(leverage)
1 2 3 4 5 6
0.23265338 0.13750066 0.09221031 0.05589964 0.04240741 0.07463096
range(leverage)
[1] 0.01066563 0.23265338
h_critical <- 2 * p / n
big_leverage <- leverage > h_critical
leverage[big_leverage]
1 2 3 6 43 53 62
0.23265338 0.13750066 0.09221031 0.07463096 0.15735648 0.07461053 0.08509555
167 169 170 171 172
0.10340534 0.07523947 0.10032390 0.14468732 0.13843795
We can plot these points:
par(mfrow = c(1, 2))
plot(
used_cars$age,
used_cars$price,
main = 'Scatter plot: sales price versus age in months',
xlab = 'age in months',
ylab = 'sales prince in 1000 Euro'
)
points(used_cars$age[big_leverage], used_cars$price[big_leverage], col = 'red', pch = 16)
plot(
used_cars$kilometer,
used_cars$price,
main = 'Scatter plot: sales price versus kilometer reading',
xlab = 'kilometer reading in 1000 km',
ylab = 'sales prince in 1000 Euro'
)
points(used_cars$kilometer[big_leverage], used_cars$price[big_leverage], col = 'red', pch = 16)

And the Cook’s Distance:
\[
D_{i} = \frac{{\left ( \hat{y}_{(i)} - \hat{y} \right )}^{T} \left (
\hat{y}_{(i)} - \hat{y} \right )}{p \cdot {\hat{\sigma}}^{2}}
\]
cook <- cooks.distance(used_cars_model[[9]])
head(cook)
1 2 3 4 5 6
0.0001119627 0.0474394511 0.0566528215 0.0008099303 0.0087695413 0.0045889281
range(cook)
[1] 1.041578e-07 1.171480e-01
big_cook <- cook > 0.5
cook[big_cook]
named numeric(0)
Example 3.24 Prices of Used Cars — Alternative Models
Adjust model 9 for all observations, for \(\text{kilometer} \leq 230\) and \(\text{kilometer} \geq 50\):
model_230 <- lm(price ~ kilometerop1 + kilometerop2 + ageop1 + ageop2, data = used_cars[used_cars$kilometer <= 230, ])
model_50 <- lm(price ~ kilometerop1 + kilometerop2 + ageop1 + ageop2, data = used_cars[used_cars$kilometer >= 50, ])
new_kilometer_effect <- function(kilometer, beta){
i_poly_killometer <- predict(poly_kilometer, kilometer)
beta[2] * i_poly_killometer[, 1] + beta[3] * i_poly_killometer[, 2]
}
plot(
kilometer_seq,
kilometer_effect(kilometer_seq),
main = '',
xlab = 'kilometer reading in 1000 km',
ylab = 'effects',
type = 'l',
lwd = 2
)
lines(kilometer_seq, new_kilometer_effect(kilometer_seq, model_230$coefficients), col = 'red', lwd = 2)
lines(kilometer_seq, new_kilometer_effect(kilometer_seq, model_50$coefficients), col = 'green', lwd = 2)
legend(
"topright",
legend = c('LS with all observation', 'LS without kilometer>230', 'LS without kilometer<50'),
col = c('black', 'red', 'green'),
lwd = 2
)

LS0tCnRpdGxlOiAiQ2xhc3NpY2FsIExpbmVhciBNb2RlbCIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CnNldC5zZWVkKDEyMykKYGBgCgojIyBIb21vc2NlZGFzdGljIEVycm9yIFZhcmlhbmNlcwoKRm9yIHRoZSB2aXN1YWxpemF0aW9uIG9mIGhvbW9jZWRhc3RpYyBhbmQgaGV0ZXJjZWRhc3RpYyB2YXJpYW5jZSwgd2Ugc2ltdWxhdGU6CgokJAp5IFxzaW0gTiBcbGVmdCAoIC0xICsgMngsIDEgXHJpZ2h0ICkKJCQKClRoZSBmdW5uZWwtc2hhcGVkIGhldGVyb2NlZGFzdGljeSBpcyBzaW11bGF0ZWQgdXNpbmc6CgokJAp5IFxzaW0gTiBcbGVmdCAoIC0xICsgMngsIFxsZWZ0ICggMC4xICsgMC4zIFxsZWZ0ICggeCArIDMgXHJpZ2h0ICkgXHJpZ2h0ICleezJ9IFxyaWdodCApCiQkCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTgsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGFyKG1mcm93ID0gYygyLCAyKSkKCiMgSG9tZWNlZGFzdGljIHZhcmlhbmNlClggPC0gc2VxKC0yLCAyLCBsZW5ndGgub3V0ID0gMzAwKQoKaG9tZWNlZGFzdGljX3JhbmRvbSA8LSBmdW5jdGlvbiAoeCl7CiAgcm5vcm0oMSwgbWVhbiA9IC0xICsgMiAqIHgsICBzZCA9IDEpWzFdCn0KCmhvbW9feSA8LSBzYXBwbHkoWCwgaG9tZWNlZGFzdGljX3JhbmRvbSkKaG9tb19lIDwtICgtMSArIDIgKiBYKSAtIGhvbW9feQoKcGxvdChYLCBob21vX3ksIHhsYWI9JycsIHlsYWI9JycsIG1haW49J2hvbW9jZWRhc3RpYyB2YXJpYW5jZScpCmFibGluZSgtMSwgMiwgY29sID0gInJlZCIsIGx3ZD0yKQpwbG90KFgsIGhvbW9fZSwgeGxhYj0nJywgeWxhYj0nJywgbWFpbj0naG9tb2NlZGFzdGljIHZhcmlhbmNlIGVycm9ycycpCmFibGluZSgwLCAwLCBjb2wgPSAicmVkIiwgbHdkPTIpCgpoZXRlcm9jZWRhc3RpY19yYW5kb20gPC0gZnVuY3Rpb24gKHgpewogIHJub3JtKDEsIG1lYW4gPSAtMSArIDIgKiB4LCBzZCA9IDAuMSArIDAuMyAqICh4ICsgMykpWzFdCn0KCmhldGVyb195IDwtIHNhcHBseShYLCBoZXRlcm9jZWRhc3RpY19yYW5kb20pCmhldGVyb19lIDwtICgtMSArIDIgKiBYKSAtIGhldGVyb195CgpwbG90KFgsIGhldGVyb195LCB4bGFiPScnLCB5bGFiPScnLCBtYWluPSdmdW5uZWwtc2hhcGVkIGhldGVyb2NlZGFzdGljIHZhcmlhbmNlJykKYWJsaW5lKC0xLCAyLCBjb2wgPSAicmVkIiwgbHdkPTIpCnBsb3QoWCwgaGV0ZXJvX2UsIHhsYWI9JycsIHlsYWI9JycsIG1haW49J2Z1bm5lbC1zaGFwZWQgaGV0ZXJvY2VkYXN0aWMgdmFyaWFuY2UgZXJyb3JzJykKYWJsaW5lKDAsIDAsIGNvbCA9ICJyZWQiLCBsd2Q9MikKYGBgCgojIyBFeGFtcGxlIDMuMSBNdW5pY2ggUmVudCBJbmRleOKAlEhldGVyb3NjZWRhc3RpYyBWYXJpYW5jZXMKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDEsIDIpKQoKbXVuaWNoX3JlbnRfdXJsIDwtICJodHRwczovL3d3dy51bmktZ29ldHRpbmdlbi5kZS9kZS9kb2N1bWVudC9kb3dubG9hZC82NGMyOWMxYjFmY2NiMTQyY2ZhOGYyOWE5NDJhOWUwNS5yYXcvcmVudDk5LnJhdyIKCm11bmljaF9yZW50X2luZGV4IDwtIHJlYWQudGFibGUoCiAgdXJsKG11bmljaF9yZW50X3VybCksCiAgaGVhZGVyID0gMSwKICBjb2xDbGFzc2VzID0gYygKICAgICJudW1lcmljIiwgIm51bWVyaWMiLCAibnVtZXJpYyIsCiAgICAibnVtZXJpYyIsICJmYWN0b3IiLCAiZmFjdG9yIiwKICAgICJmYWN0b3IiLCAiZmFjdG9yIiwgImZhY3RvciIKICApCikKCnNpbXBsZV9yZWcgPC0gbG0ocmVudCB+IGFyZWEsIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKCnBsb3QobXVuaWNoX3JlbnRfaW5kZXgkYXJlYSwgbXVuaWNoX3JlbnRfaW5kZXgkcmVudCwgeGxhYiA9ICdhcmVhIGluIHNxbScsIHlsYWIgPSAnbmV0IHJlbnQgaW4gRXVybycpCmFibGluZShzaW1wbGVfcmVnLCBjb2wgPSAicmVkIiwgbHdkID0gMikKCnByZWRpY3RlZCA8LSBwcmVkaWN0KHNpbXBsZV9yZWcpCgpwbG90KG11bmljaF9yZW50X2luZGV4JGFyZWEsIHByZWRpY3RlZCAtIG11bmljaF9yZW50X2luZGV4JHJlbnQsIHhsYWIgPSAnYXJlYSBpbiBzcW0nLCB5bGFiID0gJ3Jlc2lkdWFscycpCmFibGluZSgwLCAwLCBjb2wgPSAicmVkIiwgbHdkID0gMikKYGBgCgojIyBVbmNvcnJlbGF0ZWQgRXJyb3JzCgpTaW11bGF0ZWQgYXV0b2NvcnJlbGF0ZWQgZXJyb3JzIHdpdGggcG9zaXRpdmUgY29ycmVsYXRpb24gYXJlIGdlbmVyYXRlZCB1c2luZzoKCiQkCnlfe2l9ID0gLTEgKyAyIHhfe2l9ICsgXGVwc2lsb25fe2l9CiQkCiQkClx0ZXh0e1doZXJlIH0gXGVwc2lsb25fe2l9ID0gMC45IFxlcHNpbG9uX3tpIC0gMX0gKyBcbXVfe2l9IFx3ZWRnZSBcbXVfe2l9IFxzaW0gTigwLCAwLjVeezJ9KQokJAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMSwgMikpCgpuIDwtIDEwMApYIDwtIHNlcSgtMywgMywgbGVuZ3RoLm91dCA9IG4pCm11IDwtIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDAuNSkKCmVwc2lsb24gPC0gbXVbMV0KZm9yKGkgaW4gc2VxKDIsIG4pKXsKICBlcHNpbG9uIDwtIGMoZXBzaWxvbiwgMC45ICogZXBzaWxvbltpIC0gMV0gKyBtdVtpXSkKfQoKeV9wcmVkIDwtIC0xICsgMiAqIFggKyBlcHNpbG9uCgpwbG90KFgsIHlfcHJlZCwgeGxhYiA9ICd4JywgeWxhYiA9ICcnLCBtYWluID0gJ29ic2VydmF0aW9ucyBhbmQgcmVncmVzc2lvbiBsaW5lJykKYWJsaW5lKC0xLCAyLCBjb2wgPSAicmVkIiwgbHdkID0gMikKCnBsb3QoZXBzaWxvbiwgeGxhYiA9ICdpJywgeWxhYiA9ICcnLCBtYWluID0gJ3RpbWUgc2VyaWVzIG9mIHRoZSBlcnJvcnMnKQphYmxpbmUoMCwgMCwgY29sID0gInJlZCIsIGx3ZCA9IDIpCmBgYAoKU2ltdWxhdGVkIGF1dG9jb3JyZWxhdGVkIGVycm9ycyB3aXRoIG5lZ2F0aXZlIGNvcnJlbGF0aW9uIGFyZSBnZW5lcmF0ZWQgdXNpbmc6CgokJAp5X3tpfSA9IC0xICsgMiB4X3tpfSArIFxlcHNpbG9uX3tpfQokJAokJApcdGV4dHtXaGVyZSB9IFxlcHNpbG9uX3tpfSA9IC0gMC45IFxlcHNpbG9uX3tpIC0gMX0gKyBcbXVfe2l9IFx3ZWRnZSBcbXVfe2l9IFxzaW0gTigwLCAwLjVeezJ9KQokJAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMSwgMikpCgpuIDwtIDEwMApYIDwtIHNlcSgtMywgMywgbGVuZ3RoLm91dCA9IG4pCm11IDwtIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDAuNSkKCmVwc2lsb24gPC0gbXVbMV0KZm9yKGkgaW4gc2VxKDIsIG4pKXsKICBlcHNpbG9uIDwtIGMoZXBzaWxvbiwgLTAuOSAqIGVwc2lsb25baSAtIDFdICsgbXVbaV0pCn0KCnlfcHJlZCA8LSAtMSArIDIgKiBYICsgZXBzaWxvbgoKcGxvdChYLCB5X3ByZWQsIHhsYWIgPSAneCcsIHlsYWIgPSAnJywgbWFpbiA9ICdvYnNlcnZhdGlvbnMgYW5kIHJlZ3Jlc3Npb24gbGluZScpCmFibGluZSgtMSwgMiwgY29sID0gInJlZCIsIGx3ZCA9IDIpCgpwbG90KGVwc2lsb24sIHhsYWIgPSAnaScsIHlsYWIgPSAnJywgbWFpbiA9ICd0aW1lIHNlcmllcyBvZiB0aGUgZXJyb3JzJykKYWJsaW5lKDAsIDAsIGNvbCA9ICJyZWQiLCBsd2QgPSAyKQpgYGAKCk1pc3BlY2lmaWVkIG1vZGVsIGlzIHNpbXVsYXRlZCB1c2luZzoKCiQkCnlfe2l9ID0gXHNpbih4X3tpfSkgKyB4X3tpfSArIFxlcHNpbG9uX3tpfQokJAokJApcdGV4dHtXaGVyZSB9IFxlcHNpbG9uX3tpfSBcc2ltIE4oMCwgMC4zXnsyfSkKJCQKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDIsIDIpKQoKbiA8LSAxMDAKWCA8LSBzZXEoLTMsIDMsIGxlbmd0aC5vdXQgPSBuKQplcHNpbG9uIDwtIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDAuMykKCnkgPC0gc2luKFgpICsgWCArIGVwc2lsb24KCnBsb3QoWCwgeSwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnb2JzZXJ2YXRpb25zIGFuZCB0cnVlIGZ1bmN0aW9uJykKbGluZXMoWCwgc2luKFgpICsgWCwgY29sID0gInJlZCIsIGx3ZCA9IDIpCgpsaW5lYXJfbW9kZWwgPC0gbG0oeSB+IFgpCgpwbG90KFgsIHksIHhsYWIgPSAnJywgeWxhYiA9ICcnLCBtYWluID0gJ29ic2VydmF0aW9ucyBhbmQgcmVncmVzc2lvbiBsaW5lJykKYWJsaW5lKGxpbmVhcl9tb2RlbCwgY29sID0gInJlZCIsIGx3ZCA9IDIpCgp5X3ByZWQgPC0gcHJlZGljdChsaW5lYXJfbW9kZWwsIGRhdGEgPSBYKQpyZXMgPC0geV9wcmVkIC0geQpwbG90KFgsIHJlcywgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAncmVzaWR1YWxzJykKYWJsaW5lKDAsIDAsIGNvbCA9ICJyZWQiLCBsd2QgPSAyKQpgYGAKCiMjIEFkZGl0aXZpdHkgb2YgRXJyb3JzCgpXZSBzaW11bGF0ZSBkYXRhIHVzaW5nOgoKJCQKeV97aX0gPSBcZXhwKDEgKyB4X3tpMX0gLSB4X3tpMn0gKyBcZXBzaWxvbl97aX0pCiQkCgpXaXRoOgokJApcZXBzaWxvbl97aX0gXHNpbSBOIFxsZWZ0ICggMCwgezAuNH1eezJ9IFxyaWdodCApCiQkCgpXZSBwbG90IHRoaXMgbW9kZWw6CgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTgsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGFyKG1mcm93ID0gYygyLCAyKSkKCm4gPC0gNTAKCngxIDwtIHNlcSgwLCAzLCBsZW5ndGgub3V0ID0gbikKeDIgPC0gc2VxKDAsIDMsIGxlbmd0aC5vdXQgPSBuKQp4X2dyaWQgPC0gZXhwYW5kLmdyaWQoeDEgPSB4MSwgeDIgPSB4MikKZXBzaWxvbiA8LSBybm9ybShuXjIsIG1lYW4gPSAwLCBzZCA9IDAuNCkKeSA8LSBleHAoMSArIHhfZ3JpZCR4MSAtIHhfZ3JpZCR4MiArIGVwc2lsb24pCgpwbG90KAogIHhfZ3JpZCR4MSwgeSwKICBtYWluID0gJ3NjYXR0ZXIgcGxvdDogeSB2ZXJzdXMgeDEnLAogIHhsYWIgPSAneDEnLAogIHlsYWIgPSAneScKKQpwbG90KAogIHhfZ3JpZCR4MiwgeSwKICBtYWluID0gJ3NjYXR0ZXIgcGxvdDogeSB2ZXJzdXMgeDInLAogIHhsYWIgPSAneDInLAogIHlsYWIgPSAneScKKQoKcGxvdCgKICB4X2dyaWQkeDEsIGxvZyh5KSwKICBtYWluID0gJ3NjYXR0ZXIgcGxvdDogbG9nKHkpIHZlcnN1cyB4MScsCiAgeGxhYiA9ICd4MScsCiAgeWxhYiA9ICdsb2coeSknCikKcGxvdCgKICB4X2dyaWQkeDIsIGxvZyh5KSwKICBtYWluID0gJ3NjYXR0ZXIgcGxvdDogbG9nKHkpIHZlcnN1cyB4MicsCiAgeGxhYiA9ICd4MicsCiAgeWxhYiA9ICdsb2coeSknCikKYGBgCgojIyBFeGFtcGxlIDMuMiBTdXBlcm1hcmtldCBTY2FubmVyIERhdGEKCkRhdGEgbm90IGF2YWlsYWJsZSBvbmxpbmUuCgojIyBFeGFtcGxlIDMuMyBNdW5pY2ggUmVudCBJbmRleCDigJQgVmFyaWFibGUgVHJhbnNmb3JtYXRpb24KCkltcG9ydGluZyBkYXRhIHVzaW5nIHNjcmlwdDoKCmBgYHtyfQpzb3VyY2UoImltcG9ydF9kYXRhL211bmljaF9yZW50X2luZGV4LlIiKQpgYGAKCldlIGRvIHRoZSByZWdyZXNzaW9uIG1vZGVsOgoKJCQKXHRleHR7cmVudHNxbX1fe2l9ID0gXGJldGFfezB9ICsgXGJldGFfezF9IFxjZG90IGYoXHRleHR7YXJlYX1fe2l9KSArIFxlcHNpbG9uX3tpfQokJAoKV2l0aCBib3RoICRmKFxjZG90KSQ6CgokJApmKFx0ZXh0e2FyZWF9X3tpfSkgPSBcdGV4dHthcmVhfV97aX0KJCQKCkZvciBsaW5lYXIgbW9kZWwgYW5kOgoKJCQKZihcdGV4dHthcmVhfV97aX0pID0gXGZyYWN7MX17XHRleHR7YXJlYX1fe2l9fQokJAoKRm9yIGZ1dHVyZSB1c2Ugd2UgZGVmaW5lIHRob3NlIG1vZGVsczoKCioqTW9kZWwgMSAoJE0xJCkqKjoKCiQkClxtYXRoYmJ7RX0oXHRleHR7cmVudHFzbX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2FyZWF9CiQkCgoqKk1vZGVsIDIgKCRNMiQpKio6CgokJApcbWF0aGJie0V9KFx0ZXh0e3JlbnRxc219KSA9IFxiZXRhX3swfSArIFxiZXRhX3sxfSBcZnJhY3sxfXtcdGV4dHthcmVhfX0KJCQKCkZvciBub25saW5lYXIgcmVsYXRpb25zaGlwIGJldHdlZW4gJFx0ZXh0e3JlbnRzcW19JCBhbmQgJFx0ZXh0e2FyZWF9JC4gVGhlIHBsb3R0ZWQgZ3JhcGggYmVsb3cgYXJlIHRob3NlIHR3byBtb2RlbHMgd2l0aCB0aGUgbGVmdCBwYW5lbHMgdGhlIGxpbmVhciByZWdyZXNzaW9uIHdpdGhvdXQgdGhlIHRyYW5zZm9ybWF0aW9uIGFuZCB0aGUgcmlnaHQgcGFuZWxzIHdpdGggdGhlIGludmVyc2UgdHJhbnNmb3JtYXRpb24uCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyLCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMywgMikpCgptMSA8LSBsbShyZW50c3FtIH4gYXJlYSwgZGF0YSA9IG11bmljaF9yZW50X2luZGV4KQpzdW1tYXJ5KG0xKQoKbTIgPC0gbG0ocmVudHNxbSB+IEkoMSAvIGFyZWEpLCBkYXRhID0gbXVuaWNoX3JlbnRfaW5kZXgpCnN1bW1hcnkobTIpCgpwbG90X3JlbnRzcW1fYXJlYSA8LSBmdW5jdGlvbih0aXRsZSkgewogIHBsb3QoCiAgICBtdW5pY2hfcmVudF9pbmRleCRhcmVhLAogICAgbXVuaWNoX3JlbnRfaW5kZXgkcmVudHNxbSwKICAgIG1haW4gPSB0aXRsZSwKICAgIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogICAgeWxhYiA9ICdyZW50IHBlciBzcW0nCiAgKQp9CgpzZXFfYXJlYSA8LSBzZXEobWluKG11bmljaF9yZW50X2luZGV4JGFyZWEpLCBtYXgobXVuaWNoX3JlbnRfaW5kZXgkYXJlYSksIGxlbmd0aC5vdXQgPSAxMDApCgpwbG90X3JlbnRzcW1fYXJlYSgncmVudCBwZXIgc3FtIHZzLiBhcmVhJykKbTFfYmV0YSA8LSBtMSRjb2VmZmljaWVudHMKbGluZXMoc2VxX2FyZWEsIG0xX2JldGFbMV0gKyBtMV9iZXRhWzJdICogc2VxX2FyZWEsIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKcGxvdF9yZW50c3FtX2FyZWEoJ2YoYXJlYSkgPSAxL2FyZWEnKQptMl9iZXRhIDwtIG0yJGNvZWZmaWNpZW50cwpsaW5lcyhzZXFfYXJlYSwgbTJfYmV0YVsxXSArIG0yX2JldGFbMl0gKiAxIC8gc2VxX2FyZWEsIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKcGxvdF9yZXNpZHVhbHMgPC0gZnVuY3Rpb24obW9kZWwpIHsKICBwbG90KAogICAgbXVuaWNoX3JlbnRfaW5kZXgkYXJlYSwKICAgIG1vZGVsJHJlc2lkdWFscywKICAgIG1haW4gPSAncmVzaWR1YWxzJywKICAgIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogICAgeWxhYiA9ICdyZXNpZHVhbHMnCiAgKQogIGFibGluZSgwLCAwLCBjb2wgPSAncmVkJywgbHdkID0gMikKfQoKcGxvdF9yZXNpZHVhbHMobTEpCnBsb3RfcmVzaWR1YWxzKG0yKQoKcGxvdF9hdl9yZXNpZHVhbHMgPC0gZnVuY3Rpb24obW9kZWwpIHsKICBhdl9yZXNpZHVhbHMgPC0gdGFwcGx5KAogICAgbW9kZWwkcmVzaWR1YWxzLCBtdW5pY2hfcmVudF9pbmRleCRhcmVhLAogICAgbWVhbgogICkKICBwbG90KAogICAgdW5pcXVlKG11bmljaF9yZW50X2luZGV4JGFyZWEpLAogICAgYXZfcmVzaWR1YWxzLAogICAgbWFpbiA9ICdhdmVyYWdlIHJlc2lkdWFscycsCiAgICB4bGFiID0gJ2FyZWEgaW4gc3FtJywKICAgIHlsYWIgPSAncmVzaWR1YWxzJwogICkKICBhYmxpbmUoMCwgMCwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCn0KCnBsb3RfYXZfcmVzaWR1YWxzKG0xKQpwbG90X2F2X3Jlc2lkdWFscyhtMikKYGBgCgoKIyMgRXhhbXBsZSAzLjQgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIFBvbHlub21pYWwgUmVncmVzc2lvbgoKRm9yIHBvbHlub21pYWwgcmVncmVzc2lvbnMgd2UgYWRqdXN0IHR3byBkaWZmZXJlbnQgbW9kZWwuIE9mIHNlY29uZC1vcmRlciAoKipNb2RlbCAzICgkTTMkKSoqKToKCiQkClxtYXRoYmJ7RX0oXHRleHR7cmVudHFzbX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2FyZWF9ICsgXGJldGFfezJ9IFx0ZXh0e2FyZWF9XnsyfQokJAoKQW5kIHRoaXJkLW9yZGVyICgqKk1vZGVsIDQgKCRNNCQpKiopOgoKJCQKXG1hdGhiYntFfShcdGV4dHtyZW50cXNtfSkgPSBcYmV0YV97MH0gKyBcYmV0YV97MX0gXHRleHR7YXJlYX0gKyBcYmV0YV97Mn0gXHRleHR7YXJlYX1eezJ9ICsgXGJldGFfezN9IFx0ZXh0e2FyZWF9XnszfQokJAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMiwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDIsIDIpKQoKbTMgPC0gbG0ocmVudHNxbSB+IGFyZWEgKyBJKGFyZWFeMiksIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKc3VtbWFyeShtMykKCm00IDwtIGxtKHJlbnRzcW0gfiBhcmVhICsgSShhcmVhXjIpICsgSShhcmVhXjMpLCBkYXRhID0gbXVuaWNoX3JlbnRfaW5kZXgpCnN1bW1hcnkobTQpCgpwbG90X3JlbnRzcW1fYXJlYSgncmVudCBwZXIgc3FtIHZzLiBhcmVhJykKbTNfYmV0YSA8LSBtMyRjb2VmZmljaWVudHMKbGluZXMoc2VxX2FyZWEsIG0zX2JldGFbMV0gKyBtM19iZXRhWzJdICogc2VxX2FyZWEgKyBtM19iZXRhWzNdICogc2VxX2FyZWEgXiAyLCBjb2wgPSAncmVkJywgbHdkID0gMikKCnBsb3RfcmVudHNxbV9hcmVhKCdmKGFyZWEpID0gMS9hcmVhJykKbTRfYmV0YSA8LSBtNCRjb2VmZmljaWVudHMKbGluZXMoc2VxX2FyZWEsIG00X2JldGFbMV0gKyBtNF9iZXRhWzJdICogc2VxX2FyZWEgKyBtNF9iZXRhWzNdICogc2VxX2FyZWEgXiAyICsgbTRfYmV0YVs0XSAqIHNlcV9hcmVhIF4gMywgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCgpwbG90X3Jlc2lkdWFscyhtMykKcGxvdF9yZXNpZHVhbHMobTQpCmBgYAoKIyMgRXhhbXBsZSAzLjUgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIEFkZGl0aXZlIE1vZGVscwoKV2UgZGVmaW5lIHRoZSBmb2xsb3dpbmcgYWRpdGl2ZSBtb2RlbHMuIEFkZGl0aXZlIG1vZGVsIDE6CgokJApcbWF0aGNhbHtFfShcdGV4dHtyZW50c3FtfV97aX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFxjZG90IFxmcmFjezF9e1x0ZXh0e2FyZWF9X3tpfX0gKyBcYmV0YV97Mn0gXGNkb3QgXHRleHR7eWVhcmN9X3tpfQokJAoKQW5kIGEgc2Vjb25kIG9uZSB3aXRoICRcdGV4dHt5ZWFyY30kIGFzIGEgcG9saW5vbWlhbCBvZiBkZWdyZWUgMzoKCiQkClxtYXRoY2Fse0V9KFx0ZXh0e3JlbnRzcW19X3tpfSkgPSBcYmV0YV97MH0gKyBcYmV0YV97MX0gXGNkb3QgXGZyYWN7MX17XHRleHR7YXJlYX1fe2l9fSArIFxiZXRhX3syfSBcY2RvdCBcdGV4dHt5ZWFyY31fe2l9ICsgXGJldGFfezN9IFxjZG90IHtcdGV4dHt5ZWFyY31fe2l9fV57Mn0gKyBcYmV0YV97NH0gXGNkb3Qge1x0ZXh0e3llYXJjfV97aX19XnszfQokJAoKYGBge3J9CmFkZGl0aXZlX20xIDwtIGxtKHJlbnRzcW0gfiBJKDEvYXJlYSkgKyB5ZWFyYywgZGF0YSA9IG11bmljaF9yZW50X2luZGV4KQpzdW1tYXJ5KGFkZGl0aXZlX20xKQoKYWRkaXRpdmVfbTIgPC0gbG0ocmVudHNxbSB+IEkoMS9hcmVhKSArIHllYXJjICsgSSh5ZWFyYyBeIDIpKyBJKHllYXJjIF4gMyksIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKc3VtbWFyeShhZGRpdGl2ZV9tMikKYGBgCgpBIHRoaXJkIG1vZGVsIGlzIHNwZWNpZmllZCB1c2luZyB0aGUgdHJ1bmNhdGVkIHllYXIgb2YgY29uc3RydWN0aW9uICgkXHRleHR7eWVhcmN9IC0gMTkwMCQpOgoKYGBge3J9Cm11bmljaF9yZW50X2luZGV4JHllYXJjbyA8LSBtdW5pY2hfcmVudF9pbmRleCR5ZWFyYyAtIDE5MDAKYWRkaXRpdmVfbTMgPC0gbG0ocmVudHNxbSB+IEkoMS9hcmVhKSArIHllYXJjbyArIEkoeWVhcmNvIF4gMikrIEkoeWVhcmNvIF4gMyksIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKc3VtbWFyeShhZGRpdGl2ZV9tMykKYGBgCgpBcyBpbiB0aGUgYm9vaywgd2Ugc2hvdyB0aGUgZWZmZWN0IG9mIHllYXIgb2YgY29uc3RydWN0aW9uIGluIHRoZSBwb2x5bm9taWFsIG9mIGRlZ3JlZSAzOgoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMSwgMikpCmJldGFfYWRkX20yIDwtIGFkZGl0aXZlX20yJGNvZWZmaWNpZW50cwoKeWVhcmNfc2VxIDwtIHNlcShtaW4obXVuaWNoX3JlbnRfaW5kZXgkeWVhcmMpLCBtYXgobXVuaWNoX3JlbnRfaW5kZXgkeWVhcmMpLCBsZW5ndGgub3V0ID0gMTAwKQoKcGxvdF9lZmZlY3QgPC0gZnVuY3Rpb24oYmV0YSwgeWVhcl9zZXEsIHRpdGxlKXsKICBwbG90KAogICAgeWVhcl9zZXEsCiAgICBiZXRhWzNdICogeWVhcl9zZXEgKyBiZXRhWzRdICogeWVhcl9zZXFeMiArIGJldGFbNV0gKiB5ZWFyX3NlcV4zLAogICAgbWFpbiA9IHRpdGxlLAogICAgeGxhYiA9ICd5ZWFyIG9mIGNvbnN0cnVjdGlvbicsCiAgICB5bGFiID0gJ2VmZmVjdCBvZiB5ZWFyIG9mIGNvbnN0cnVjdGlvbicsCiAgICB0eXBlID0gJ2wnLAogICAgY29sID0gJ2RhcmtibHVlJywKICAgIGx3ZCA9IDIKICApCn0KCnBsb3RfZWZmZWN0KGJldGFfYWRkX20yLCB5ZWFyY19zZXEsICdlZmZlY3Qgb2YgeWVhciBvZiBjb25zdHJ1Y3Rpb24nKQoKYmV0YV9hZGRfbTMgPC0gYWRkaXRpdmVfbTMkY29lZmZpY2llbnRzCnRydW5jYXRlX3llYXJjX3NlcSA8LSBzZXEobWluKG11bmljaF9yZW50X2luZGV4JHllYXJjbyksIG1heChtdW5pY2hfcmVudF9pbmRleCR5ZWFyY28pLCBsZW5ndGgub3V0ID0gMTAwKQpwbG90X2VmZmVjdChiZXRhX2FkZF9tMywgdHJ1bmNhdGVfeWVhcmNfc2VxLCAnZWZmZWN0IG9mIHllYXIgb2YgY29uc3RydWN0aW9uLCBjb2RlZCBmcm9tIDE4IHRvIDk2JykKYGBgCgpBIG5ldyBtb2RlbCBpcyBkZWZpbmVkIHVzaW5nIHRoZSBvcnRob2dvbmFsIHBvbHlub21pYWwgb2YgeWVhciBvZiBjb25zdHJ1Y3Rpb246CgpgYGB7cn0KbXVuaWNoX3JlbnRfaW5kZXgkYXJlYWludiA8LSAoMSAvIG11bmljaF9yZW50X2luZGV4JGFyZWEpIC0gbWVhbigxIC8gbXVuaWNoX3JlbnRfaW5kZXgkYXJlYSkKcG9seV95ZWFyIDwtIHBvbHkobXVuaWNoX3JlbnRfaW5kZXgkeWVhcmNvLCAzKQptdW5pY2hfcmVudF9pbmRleCR5ZWFyY2MgPC0gcG9seV95ZWFyWywgMV0KbXVuaWNoX3JlbnRfaW5kZXgkeWVhcmNjMiA8LSBwb2x5X3llYXJbLCAyXQptdW5pY2hfcmVudF9pbmRleCR5ZWFyY2MzIDwtIHBvbHlfeWVhclssIDNdCmFkZGl0aXZlX200IDwtIGxtKHJlbnRzcW0gfiBhcmVhaW52ICsgeWVhcmNjICsgeWVhcmNjMiArIHllYXJjYzMsIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleCkKc3VtbWFyeShhZGRpdGl2ZV9tNCkKYGBgCgpOb3cgd2UgY2FsY3VsYXRlIHRoZSBwYXJ0aWFsIHJlc2lkdWFscyBmb3IgJFx0ZXh0e2FyZWF9JCBhbmQgJFx0ZXh0e3llYXJjfSQgZGVmaW5lIGJ5OgoKJCQKXGhhdHtcZXBzaWxvbn1fe1x0ZXh0e2FyZWF9LCBpfSA9IFx0ZXh0e3JlbnRzcW19X3tpfSAtIFxoYXR7XGJldGFfezB9fSAtIFxoYXR7Zn1fezJ9KFx0ZXh0e3llYXJjfV97aX0pCiQkCgpXaXRoIHRoZSBlZmZlY3QgJGYoXHRleHR7eWVhcmN9X3tpfSkkOgoKJCQKXGhhdHtmfV97Mn0oXHRleHR7eWVhcmN9KSA9IFxiZXRhX3syfSBcY2RvdCBcdGV4dHt5ZWFyY31fe2l9ICsgXGJldGFfezN9IFxjZG90IHtcdGV4dHt5ZWFyY31fe2l9fV57Mn0gKyBcYmV0YV97NH0gXGNkb3Qge1x0ZXh0e3llYXJjfV97aX19XnszfQokJAoKQW5kOgoKJCQKXGhhdHtcZXBzaWxvbn1fe1x0ZXh0e3llYXJjfSwgaX0gPSBcdGV4dHtyZW50c3FtfV97aX0gLSBcaGF0e1xiZXRhX3swfX0gLSBcaGF0e2Z9X3sxfShcdGV4dHthcmVhfV97aX0pCiQkCgpXaXRoIHRoZSBlZmZlY3QgJGYoXHRleHR7eWVhcmN9X3tpfSkkOgoKJCQKXGhhdHtmfV97MX0oXHRleHR7eWVhcmN9KSA9IFxiZXRhX3sxfSBcY2RvdCBcZnJhY3sxfXtcdGV4dHthcmVhfV97aX19CiQkCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGFyKG1mcm93ID0gYygxLCAyKSkKCmJldGFfYWRkX200IDwtIGFkZGl0aXZlX200JGNvZWZmaWNpZW50cwoKYXJlYV9zZXEgPC0gc2VxKG1pbihtdW5pY2hfcmVudF9pbmRleCRhcmVhKSwgbWF4KG11bmljaF9yZW50X2luZGV4JGFyZWEpLCBsZW5ndGgub3V0ID0gMTAwKQoKYXJlYV9lZmZlY3QgPC0gZnVuY3Rpb24oYXJlYSl7CiAgaW52X2FyZWEgPC0gMSAvIGFyZWEKICBjZW50X2FyZWEgPC0gaW52X2FyZWEgLSBtZWFuKGludl9hcmVhKQogIGJldGFfYWRkX200WzJdICogY2VudF9hcmVhCn0KCnllYXJjX2VmZmVjdCA8LSBmdW5jdGlvbih5ZWFyYyl7CiAgcG9seV95ZWFyYyA8LSBwcmVkaWN0KHBvbHlfeWVhciwgeWVhcmMgLSAxOTAwKQogIGJldGFfYWRkX200WzNdICogcG9seV95ZWFyY1ssIDFdICsgYmV0YV9hZGRfbTRbNF0gKiBwb2x5X3llYXJjWywgMl0gKyBiZXRhX2FkZF9tNFs1XSAqIHBvbHlfeWVhcmNbLCAzXQp9CgpyZXNpZHVhbF9hcmVhIDwtIG11bmljaF9yZW50X2luZGV4JHJlbnRzcW0gLSBiZXRhX2FkZF9tNFsxXSAtIHllYXJjX2VmZmVjdChtdW5pY2hfcmVudF9pbmRleCR5ZWFyYykKcGxvdCgKICBtdW5pY2hfcmVudF9pbmRleCRhcmVhLAogIHJlc2lkdWFsX2FyZWEsCiAgbWFpbiA9ICdlZmZlY3Qgb2YgYXJlYScsCiAgeGxhYiA9ICdhcmVhIGluIHNxbScsCiAgeWxhYiA9ICdlZmZlY3Qgb2YgYXJlYSAvIHBhcnRpYWwgcmVzaWR1YWxzJwopCmxpbmVzKGFyZWFfc2VxLCBhcmVhX2VmZmVjdChhcmVhX3NlcSksIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKcmVzaWR1YWxfeWVhciA8LSBtdW5pY2hfcmVudF9pbmRleCRyZW50c3FtIC0gYmV0YV9hZGRfbTRbMV0gLSBhcmVhX2VmZmVjdChtdW5pY2hfcmVudF9pbmRleCRhcmVhKQpwbG90KAogIG11bmljaF9yZW50X2luZGV4JHllYXJjLAogIHJlc2lkdWFsX3llYXIsCiAgbWFpbiA9ICdlZmZlY3Qgb2YgeWVhciBvZiBjb25zdHJ1Y3Rpb24nLAogIHhsYWIgPSAneWVhciBvZiBjb25zdHJ1Y3Rpb24nLAogIHlsYWIgPSAnZWZmZWN0IG9mIHllYXIgb2YgY29uc3RydWN0aW9uIC8gcGFydGlhbCByZXNpZHVhbHMnCikKbGluZXMoeWVhcmNfc2VxLCB5ZWFyY19lZmZlY3QoeWVhcmNfc2VxKSwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCmBgYAoKIyMgRXhhbXBsZSAzLjYgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIEVmZmVjdCBDb2RpbmcKCldlIGFkanVzdCB0aGUgcmVncmVzc2lvbiBtb2RlbCB3aXRoIHRoZSBjb2RpbmcgdmFsdWVzIG9mIGxvY2F0aW9uOgoKJCQKXG1hdGhiYntFfShcdGV4dHtyZW50c3FtfV97aX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFxjZG90IFx0ZXh0e2dsb2NhdGlvbn1fezF9ICsgXGJldGFfezJ9IFxjZG90IFx0ZXh0e3Rsb2NhdGlvbn1fezF9CiQkCgpgYGB7cn0KbXVuaWNoX3JlbnRfaW5kZXgkZ2xvY2F0aW9uIDwtIDAKbXVuaWNoX3JlbnRfaW5kZXgkZ2xvY2F0aW9uW211bmljaF9yZW50X2luZGV4JGxvY2F0aW9uID09IDJdIDwtIDEKbXVuaWNoX3JlbnRfaW5kZXgkZ2xvY2F0aW9uW211bmljaF9yZW50X2luZGV4JGxvY2F0aW9uID09IDFdIDwtIC0xCgptdW5pY2hfcmVudF9pbmRleCR0bG9jYXRpb24gPC0gMAptdW5pY2hfcmVudF9pbmRleCR0bG9jYXRpb25bbXVuaWNoX3JlbnRfaW5kZXgkbG9jYXRpb24gPT0gM10gPC0gMQptdW5pY2hfcmVudF9pbmRleCR0bG9jYXRpb25bbXVuaWNoX3JlbnRfaW5kZXgkbG9jYXRpb24gPT0gMV0gPC0gLTEKCmNvZF9tb2RlbCA8LSBsbSgKICByZW50c3FtIH4gZ2xvY2F0aW9uICsgdGxvY2F0aW9uLAogIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleAopCnN1bW1hcnkoY29kX21vZGVsKQpgYGAKCiMjIEV4YW1wbGUgMy43IE11bmljaCBSZW50IEluZGV4IOKAlCBJbnRlcmFjdGlvbiB3aXRoIFF1YWxpdHkgb2YgS2l0Y2hlbgoKV2UgaW1wb3J0IHRoZSBuZXcgdXBkYXRlZCBtb2RlbCBvZiAyMDAxLCBhdmFpbGFibGUgYXQgW29mZmljaWFsIHBhZ2Ugb2YgdGhlIGRhdGFzZXRdKGh0dHBzOi8vd3d3LnVuaS1nb2V0dGluZ2VuLmRlL2VuLzU1MTYyNS5odG1sKSBhcyBhbGwgdGhlIGRhdGFzZXRzOgoKYGBge3J9Cm11bmljaF9yZW50X3VybF8wMSA8LSAiaHR0cHM6Ly93d3cudW5pLWdvZXR0aW5nZW4uZGUvZGUvZG9jdW1lbnQvZG93bmxvYWQvZTAwZDAzOWU5YzFmMjhhYjQwNGIyN2IwYjE0OTMxZjEucmF3L3JlbnQ5OF8wMC5yYXciCgptdW5pY2hfcmVudF9pbmRleF8wMSA8LSByZWFkLnRhYmxlKAogIHVybChtdW5pY2hfcmVudF91cmxfMDEpLAogIGhlYWRlciA9IDEsCiAgY29sQ2xhc3NlcyA9IGMoCiAgICAibnVtZXJpYyIsICJudW1lcmljIiwgIm51bWVyaWMiLAogICAgIm51bWVyaWMiLCAiaW50ZWdlciIsICJpbnRlZ2VyIiwKICAgICJpbnRlZ2VyIiwgImludGVnZXIiLCAiaW50ZWdlciIsCiAgICAiaW50ZWdlciIsICJudW1lcmljIiwgIm51bWVyaWMiLAogICAgIm51bWVyaWMiCiAgKQopCgpzdW1tYXJ5KG11bmljaF9yZW50X2luZGV4XzAxKQpgYGAKCkFuZCB3ZSBhZGp1c3QgdGhlIG1vZGVsOgoKJCQKXGJlZ2lue21hdHJpeH0KICBcd2lkZWhhdHtcdGV4dHtyZW50c3FtfX1fe2l9ID0gJiBcYmV0YV97MH0gKyBcYmV0YV97MX0gXHRleHR7YXJlYWludmN9X3tpfSArIFxiZXRhX3syfSBcdGV4dHt5ZWFyY299X3tpfSArIFxiZXRhX3szfSBcdGV4dHt5ZWFyY28yfV97aX0gKyBcYmV0YV97NH0gXHRleHR7eWVhcmNvM31fe2l9IFxcCiAgICYgKyBcYmV0YV97NX0gXHRleHR7eWVhcjAxfV97aX0gKyBcYmV0YV97Nn0gXHRleHR7bmtpdGNoZW59X3tpfSArIFxiZXRhX3s3fSBcdGV4dHtwa2l0Y2hlbn1fe2l9IFxcCiAgICYgKyBcYmV0YV97OH0gXHRleHR7bmtpdGNoZW59X3tpfSBcY2RvdCBcdGV4dHt5ZWFyMDF9X3tpfSArIFxiZXRhX3s5fSBcdGV4dHtwa2l0Y2hlbn1fe2l9IFxjZG90IFx0ZXh0e3llYXIwMX1fe2l9ClxlbmR7bWF0cml4fQokJAoKYGBge3J9Cm11bmljaF9yZW50X2luZGV4XzAxJGFyZWFpbnZjIDwtIG11bmljaF9yZW50X2luZGV4XzAxJGludmFyZWEgLSBtZWFuKG11bmljaF9yZW50X2luZGV4XzAxJGludmFyZWEpCm11bmljaF9yZW50X2luZGV4XzAxJHllYXJjbyA8LSBtdW5pY2hfcmVudF9pbmRleF8wMSR5ZWFyYyAtIG1lYW4obXVuaWNoX3JlbnRfaW5kZXhfMDEkeWVhcmMpCnBvbHlfeWVhcl8wMSA8LSBwb2x5KG11bmljaF9yZW50X2luZGV4XzAxJHllYXJjbywgMykKbXVuaWNoX3JlbnRfaW5kZXhfMDEkeWVhcmNvIDwtIHBvbHlfeWVhcl8wMVssIDFdCm11bmljaF9yZW50X2luZGV4XzAxJHllYXJjbzIgPC0gcG9seV95ZWFyXzAxWywgMl0KbXVuaWNoX3JlbnRfaW5kZXhfMDEkeWVhcmNvMyA8LSBwb2x5X3llYXJfMDFbLCAzXQoKaW50ZXJhY3Rpb25fbW9kZWwgPC0gbG0oCiAgcmVudHNxbV9ldXJvIH4gYXJlYWludmMgKyB5ZWFyY28gKyB5ZWFyY28yICsgeWVhcmNvMyArIHllYXIwMSArIG5raXRjaGVuICsgcGtpdGNoZW4gKyBua2l0Y2hlbiAqIHllYXIwMSArIHBraXRjaGVuICogeWVhcjAxLAogIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleF8wMQopCgpzdW1tYXJ5KGludGVyYWN0aW9uX21vZGVsKQpgYGAKCiMjIEV4YW1wbGUgMy44IE11bmljaCBSZW50IEluZGV4IOKAlCBJbnRlcmFjdGlvbiBCZXR3ZWVuIExpdmluZyBBcmVhIGFuZCBMb2NhdGlvbgoKVGhlIG1vZGVsIHRvIGFkanVzdCBpdXMgZGVmaW5lZCBhczoKCiQkClxtYXRoYmJ7RX0oXHRleHR7cmVudHNxbX1fe2l9KSA9IFxiZXRhX3swfSArIFxiZXRhX3sxfSBcY2RvdCBcdGV4dHt0bG9jYXRpb259ICsgXGJldGFfezJ9IFxjZG90IFxvdmVybGluZXtcZnJhY3sxfXtcdGV4dHthcmVhfX19ICsgXGJldGF7M30gXGNkb3QgXHRleHR7YXJlYX0gXGNkb3QgXHRleHR7dGxvY2F0aW9ufQokJAoKV2UgdXNlIHRoZSBkYXRhc2V0IGZvciBhdmVyYWdlIG9yIHRvcCBsb2NhdGlvbiBvbmx5IGFuZCBjaGFuZ2UgdGhlIGR1bW15IHZhcmlhYmxlICRcdGV4dHt0bG9jYXRpb259JCB0byAkMCQgKGF2ZXJhZ2UpIGFuZCAkMSQgKHRvcCkuIEZpbmFsbHksIHdlIHZpc3VhbGl6ZSB0aGUgZWZmZWN0IGFzIGRlc2NyaWJlZCBpbiB0aGUgYm9vay4KCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDEsIDIpKQphdF9yZW50IDwtIHN1YnNldChtdW5pY2hfcmVudF9pbmRleCwgbG9jYXRpb24gPT0gMSB8IGxvY2F0aW9uID09IDMpCmF0X3JlbnQkdGxvY2F0aW9uW2F0X3JlbnQkbG9jYXRpb24gPT0gMV0gPC0gMApzdW1tYXJ5KGF0X3JlbnQpCgphcmVhX2xvY2F0aW9uX21vZGVsIDwtIGxtKAogIHJlbnRzcW0gfiB0bG9jYXRpb24gKyBhcmVhaW52ICsgYXJlYTp0bG9jYXRpb24sCiAgZGF0YSA9IGF0X3JlbnQKKQpzdW1tYXJ5KGFyZWFfbG9jYXRpb25fbW9kZWwpCgpiZXRhX2FsIDwtIGFyZWFfbG9jYXRpb25fbW9kZWwkY29lZmZpY2llbnRzCgpmMV9hcmVhIDwtIGZ1bmN0aW9uKGFyZWEpewogIGFyZWFpbnZjIDwtICgxIC8gYXJlYSkgLSBtZWFuKDEgLyBhcmVhKQogIGJldGFfYWxbM10gKiBhcmVhaW52Ywp9CgpmMl9hcmVhIDwtIGZ1bmN0aW9uKGFyZWEpewogIGJldGFfYWxbNF0gKiBhcmVhCn0KCnNtYWxsX2VmZmVjdCA8LSBmMV9hcmVhKGFyZWFfc2VxKQpiaWdfZWZmZWN0IDwtIGJldGFfYWxbMl0gKyBmMV9hcmVhKGFyZWFfc2VxKSArIGYyX2FyZWEoYXJlYV9zZXEpCgp5bGltIDwtIGMobWluKGMoc21hbGxfZWZmZWN0LCBiaWdfZWZmZWN0KSksIG1heChjKHNtYWxsX2VmZmVjdCwgYmlnX2VmZmVjdCkpKQoKcGxvdCgKICBhcmVhX3NlcSwKICBzbWFsbF9lZmZlY3QsCiAgbWFpbiA9ICdhcmVhIGVmZmVjdHMgYmFzZWQgb24gYSBsaW5lYXIgaW50ZXJhY3Rpb24nLAogIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogIHlsYWIgPSAnYXJlYSBlZmZlY3RzJywKICB5bGltID0geWxpbSwKICB0eXBlID0gJ2wnLAogIGNvbCA9ICdkYXJrYmx1ZScsCiAgbHdkID0gMgopCmxpbmVzKGFyZWFfc2VxLCBiaWdfZWZmZWN0LCBjb2wgPSAncmVkJywgbHdkID0gMikKCnBsb3QoCiAgYXJlYV9zZXEsCiAgYmV0YV9hbFsyXSArIGYyX2FyZWEoYXJlYV9zZXEpLAogIG1haW4gPSAndmFyeWluZyBlZmZlY3Qgb2YgdG9wIGxvY2F0aW9uJywKICB4bGFiID0gJ2FyZWEgaW4gc3FtJywKICB5bGFiID0gJ1ZhcnlpbmcgZWZmZWN0IG9mIHRvcCBsb2NhdGlvbicsCiAgdHlwZSA9ICdsJywKICBjb2wgPSAnZGFya2JsdWUnLAogIGx3ZCA9IDIKKQpgYGAKCiMjIEV4YW1wbGUgMy45IE9ydGhvZ29uYWwgRGVzaWduCgpKdXN0IGZvciBleGVtcGxpZnksIHdlIGRlZmluZSB0aGUgb3J0aG9nb25hbCBwcm9jZXNzIGRlc2NyaWJlIGFzIGFuIFIgZnVuY3Rpb246CgokJApcdGlsZGV7eH1ee2p9ID0geF57an0gLSBcd2lkZXRpbGRle1h9X3tqfSBcbGVmdCAoIHtcd2lkZXRpbGRle1h9X3tqfX1ee1R9IFx3aWRldGlsZGV7WH1fe2p9IFxyaWdodCApXnstMX0ge1x3aWRldGlsZGV7WH1fe2p9fV57VH0geF57an0KJCQKCmBgYHtyfQpvcnRob2dvbmFsX2Rlc2lnbiA8LSBmdW5jdGlvbih4KXsKICB4X291dCA8LSBjYmluZCh4WywgMV0gLSBtZWFuKHhbLCAxXSksIG1hdHJpeCgwLCBucm93ID0gbnJvdyh4KSwgbmNvbCA9IG5jb2woeCkgLSAxKSkKICBmb3IgKGkgaW4gMjpuY29sKHgpKXsKICAgIHhfaGF0IDwtIGFzLm1hdHJpeCh4X291dFssIDE6KGktMSldKQogICAgeF9pbnYgPC0gc29sdmUodCh4X2hhdCkgJSolIHhfaGF0KQogICAgeF9vdXRbLCBpXSA8LSB4WywgaV0gLSB4X2hhdCAlKiUgeF9pbnYgJSolIHQoeF9oYXQpICUqJSB4WywgaV0KICB9CiAgeF9vdXQKfQpgYGAKV2UgdGVzdCB0aGlzIGZ1bmN0aW9uIG9uIHRoZSBwb2x5bm9taWFsIG9mIHRoZSAkXHRleHR7eWVhcmN9JCB2YXJpYWJsZToKCmBgYHtyfQpwb2x5X3Rlc3QgPC0gcG9seShtdW5pY2hfcmVudF9pbmRleCR5ZWFyYywgMywgcmF3ID0gVFJVRSkKb3J0aG9nb25hbF90ZXN0IDwtIG9ydGhvZ29uYWxfZGVzaWduKHBvbHlfdGVzdCkKCiMgQW5kIGNoZWNrIGl0CmMoCiAgb3J0aG9nb25hbF90ZXN0WywgMV0gJSolIG9ydGhvZ29uYWxfdGVzdFssIDJdLAogIG9ydGhvZ29uYWxfdGVzdFssIDJdICUqJSBvcnRob2dvbmFsX3Rlc3RbLCAzXSwKICBvcnRob2dvbmFsX3Rlc3RbLCAxXSAlKiUgb3J0aG9nb25hbF90ZXN0WywgM10KKQpgYGAKCldoeSBkaWQgaXQgbm90IHJlc3VsdD8KCiMjIEV4YW1wbGUgMy4xMCBNdW5pY2ggUmVudCBJbmRleCDigJQgQ29tcGFyaXNvbiBvZiBNb2RlbHMgVXNpbmcgJHtSfV57Mn0kCgpXZSBjb21wYXJlIHRoZSBtb2RlbHM6ICRNMSQsICRNMiQsICRNMyQsIGFuZCAkTTQkIHVzaW5nIGl0cyBjb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uICRSXnsyfSQKCmBgYHtyfQpjb2VmZl9kZXRlciA8LSBkYXRhLmZyYW1lKAogIGBSIFNxdWFyZWRgID0gYyhzdW1tYXJ5KG0xKSRyLnNxdWFyZWQsIHN1bW1hcnkobTIpJHIuc3F1YXJlZCwgc3VtbWFyeShtMykkci5zcXVhcmVkLCBzdW1tYXJ5KG00KSRyLnNxdWFyZWQpLAogIHJvdy5uYW1lcyA9IGMoIk0xIiwgIk0yIiwgIk0zIiwgIk00IikKKQpjb2VmZl9kZXRlcgpgYGAKCiMjIEV4YW1wbGUgMy4xMSBTaW1wbGUgTGluZWFyIFJlZ3Jlc3Npb24KCkZvciB0aGUgbW9kZWw6CgokJAp5X3tpfSA9IFxiZXRhIHhfe2l9ICsgXGVwc2lsb25fe2l9CiQkCgpXZSBjYW4gdmVyaWZ5OgoKJCQKXGxlZnQgKCB7WF97bn19XntUfSBYX3tufSBccmlnaHQgKV57LTF9IFx0byAwCiQkCgpBbmQ6CgokJApcbWF4X3tpPTEsLi4uLG59IHt4X3tpfX1ee1R9IFxsZWZ0ICgge1hfe259fV57VH0gWF97bn0gXHJpZ2h0ICleey0xfSB4X3tpfSBcdG8gMCBcdGV4dHsgZm9yIH0gbiBcdG8gXGluZnR5CiQkCgojIyMgJHhfe2l9ID0gaSQ6CgokJApcbGVmdCAoIHtYX3tufX1ee1R9IFhfe259IFxyaWdodCApXnstMX0gPSBcbGVmdCAoICgxLCAyLCAzLCBcY2RvdHMsIG4pIFxsZWZ0ICggXGJlZ2lue21hdHJpeH0KMSBcXAoyIFxcClx2ZG90cyAgXFwKbgpcZW5ke21hdHJpeH0gXHJpZ2h0ICkgXHJpZ2h0ICleey0xfSA9IFxsZWZ0ICggMSArIDQgKyBcY2RvdHMgKyBuXjIgXHJpZ2h0ICleey0xfSBcdG8gMAokJAoKQW5kOgoKJCQKXG1heF97aT0xLC4uLixufSAoIDEsIDIsIFxjZG90cywgbiApIFxsZWZ0ICggMSArIDQgKyBcY2RvdHMgKyBuXjIgXHJpZ2h0ICleey0xfSBcbGVmdCAoIFxiZWdpbnttYXRyaXh9CjEgXFwKMiBcXApcdmRvdHMgIFxcCm4KXGVuZHttYXRyaXh9IFxyaWdodCApIFx0byAwIFx0ZXh0eyBmb3IgfSBuIFx0byBcaW5mdHkKJCQKCiMjIyAkeF97aX0gPSBcZnJhY3sxfXtpfSQ6CgokJApcbGVmdCAoIHtYX3tufX1ee1R9IFhfe259IFxyaWdodCApXnstMX0gPSBcbGVmdCAoIFxsZWZ0ICgxLCAwLjUsIDAuMzMsIFxjZG90cywgXGZyYWN7MX17bn0gXHJpZ2h0ICkgXGxlZnQgKCBcYmVnaW57bWF0cml4fQoxIFxcCjAuNSBcXApcdmRvdHMgIFxcClxmcmFjezF9e259ClxlbmR7bWF0cml4fSBccmlnaHQgKSBccmlnaHQgKV57LTF9ID0gXGxlZnQgKCBcc3VtX3tpPTF9XntufSBcZnJhY3sxfXtpXnsyfX0gXHJpZ2h0ICleey0xfSBcbm90e1x0b30gMAokJAoKQW5kOgoKJCQKXG1heF97aT0xLC4uLixufSBcbGVmdCAoIDEsIDAuNSwgXGNkb3RzLCBcZnJhY3sxfXtufSBccmlnaHQgKSBcbGVmdCAoIFxzdW1fe2kgPSAxfV57bn0gXGZyYWN7MX17aV57Mn19IFxyaWdodCApXnstMX0gXGxlZnQgKCBcYmVnaW57bWF0cml4fQoxIFxcCjAuNSBcXApcdmRvdHMgIFxcClxmcmFjezF9e259ClxlbmR7bWF0cml4fSBccmlnaHQgKSBcbm90e1x0b30gMCBcdGV4dHsgZm9yIH0gbiBcdG8gXGluZnR5CiQkCgojIyMgJHhfe2l9ID0gXGZyYWN7MX17XHNxcnR7aX19JDoKCiQkClxsZWZ0ICgge1hfe259fV57VH0gWF97bn0gXHJpZ2h0ICleey0xfSA9IFxsZWZ0ICggXHN1bV97aSA9IDF9XntufSBcZnJhY3sxfXtpfSBccmlnaHQgKV57LTF9IFx0byAwCiQkCgpBbmQ6CgokJApcbWF4X3tpPTEsLi4uLG59IFxsZWZ0ICggMSwgXGZyYWN7MX17XHNxcnR7Mn19LCBcY2RvdHMsIFxmcmFjezF9e1xzcXJ0e259fSBccmlnaHQgKSBcbGVmdCAoIFxzdW1fe2kgPSAxfV57bn0gXGZyYWN7MX17aX0gXHJpZ2h0ICleey0xfSBcYmVnaW57bWF0cml4fQoxIFxcClxmcmFjezF9e1xzcXJ0ezJ9fSBcXApcdmRvdHMgXFwKXGZyYWN7MX17XHNxcnR7bn19ClxlbmR7bWF0cml4fSBcdG8gMAokJAoKIyMgRXhhbXBsZSAzLjEyIE11bmljaCBSZW50IEluZGV4IOKAlCBIeXBvdGhlc2lzIFRlc3RpbmcKClRoZSByZWdyZXNzaW9uIG1vZGVsIHRvIGFkanVzdCBpczoKCiQkClxiZWdpbnttYXRyaXh9Clx0ZXh0e3JlbnRzcW19X3tpfSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2FyZWFpbnZjfV97aX0gKyBcYmV0YV97Mn0gXHRleHR7eWVhcmNvfV97aX0gKyBcYmV0YV97M30gXHRleHR7eWVhcmNvMn1fe2l9ICsgXGJldGFfezR9IFx0ZXh0e3llYXJjbzN9X3tpfSBcXAomICsgXGJldGFfezV9IFx0ZXh0e25raXRjaGVufV97aX0gKyBcYmV0YV97Nn0gXHRleHR7cGtpdGNoZW59X3tpfSArIFxiZXRhX3s3fSBcdGV4dHt5ZWFyMDF9X3tpfSArIFxlcHNpbG9uX3tpfQpcZW5ke21hdHJpeH0KJCQKCmBgYHtyfQpoeXBvdGhlc2lzX21vZGVsIDwtIGxtKAogIHJlbnRzcW1fZXVybyB+IGFyZWFpbnZjICsgeWVhcmNvICsgeWVhcmNvMiArIHllYXJjbzMgKyBua2l0Y2hlbiArIHBraXRjaGVuICsgeWVhcjAxLAogIGRhdGEgPSBtdW5pY2hfcmVudF9pbmRleF8wMQopCnN1bW1hcnkoaHlwb3RoZXNpc19tb2RlbCkKYGBgCgpTbyB3ZSB3YW50IHRvIHRlc3QgdGhlIGh5cG90aGVzaXM6CgokJApcYmVnaW57bWF0cml4fQpIX3swfSA6IFxiZXRhX3s3fSA9IDAgJiBcdGV4dHthZ2FpbnN0fSAmIEhfezF9IDogXGJldGFfezd9IFxuZXEgMApcZW5ke21hdHJpeH0KJCQKCkFib3V0IHRoZSBzaWduaWZpY2FuY2Ugb2YgdGhlIGZvbGxvdy11cCBtZWFzdXJlLgoKJCQKXGJlZ2lue21hdHJpeH0KSF97MH0gOiBcbGVmdCAoIFxiZWdpbnttYXRyaXh9ClxiZXRhX3s1fSBcXApcYmV0YV97Nn0KXGVuZHttYXRyaXh9IFxyaWdodCApID0gXGxlZnQgKCBcYmVnaW57bWF0cml4fQowIFxcCjAKXGVuZHttYXRyaXh9IFxyaWdodCApICYgXHRleHR7YWdhaW5zdH0gJiBIX3sxfSA6IFxsZWZ0ICggXGJlZ2lue21hdHJpeH0KXGJldGFfezV9IFxcClxiZXRhX3s2fQpcZW5ke21hdHJpeH0gXHJpZ2h0ICkgXG5lcSBcbGVmdCAoIFxiZWdpbnttYXRyaXh9CjAgXFwKMApcZW5ke21hdHJpeH0gXHJpZ2h0ICkKXGVuZHttYXRyaXh9CiQkCgpBYm91dCB0aGUgc2lnbmlmaWNhbmNlIG9mIHRoZSBraXRjaGVuIHZhcmlhYmxlLiBBbmQgYSBmaW5hbCB0ZXN0IGFib3V0IHRoZSBzaWduaWZpY2FuY2Ugb2YgZGlmZmVyZW50aWF0ZSBiZXR3ZWVuIHRoZXNlIHR3byB0eXBlIG9mIGtpdGNoZW5zOgoKJCQKXGJlZ2lue21hdHJpeH0KSF97MH0gOiBcYmV0YV97NX0gLSBcYmV0YV97Nn0gPSAwICYgXHRleHR7YWdhaW5zdH0gJiBIX3sxfSA6IFxiZXRhX3s1fSAtIFxiZXRhX3s2fSBcbmVxIDAKXGVuZHttYXRyaXh9CiQkCgojIyBFeGFtcGxlIDMuMTMgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIFN0YW5kYXJkIE91dHB1dCBhbmQgSHlwb3RoZXNpcyBUZXN0cwoKRm9yIHRoZSBmaXJzdCB0ZXN0ICgkSF97MH0gOiBcYmV0YV97N30gPSAwJCksIHdlIGNhbGN1bGF0ZToKCiQkCnRfezd9ID0gXGZyYWN7XGhhdHtcYmV0YX1fezd9fXt7XHdpZGVoYXR7XHRleHR7VmFyfShcaGF0e1xiZXRhfV97N30pfX1eezEvMn19IFxzaW0gdF97MSAtIFxhbHBoYSAvIDJ9IChuIC0gcCkKJCQKClRoZSBjb3ZhcmlhbmNlIG1hdHJpeCBpczoKCmBgYHtyfQpjb3ZfbWF0cml4IDwtIHZjb3YoaHlwb3RoZXNpc19tb2RlbCkKY292X21hdHJpeApgYGAKCldlIGNoZWNrIHRoZSAkXHRleHR7VmFyfShcaGF0e1xiZXRhfV97N30pJCBpcyB0aGUgbGFzdCB2YWx1ZS4KCmBgYHtyfQp2YXJfNyA8LSBjb3ZfbWF0cml4WzgsIDhdCnRfNyA8LSBoeXBvdGhlc2lzX21vZGVsJGNvZWZmaWNpZW50c1s4XSAvIHNxcnQodmFyXzcpCnRfNwpgYGAKCkFuZCB0aGlzIGNvaW5jaWRlcyB3aXRoIHRoZSB2YWx1ZSBnaXZlbiBpbiB0aGUgYHN1bW1hcnlgLgoKRm9yIHRoZSBzZWNvbmQgdGVzdCwgd2UgY29tcHV0ZSB0aGUgJEYkIHZhbHVlIGFzOgoKJCQKRiA9IFxmcmFjezF9e3J9IHtcaGF0e1xiZXRhfX1ee1R9IFx3aWRlaGF0e1x0ZXh0e0Nvdn0gXGxlZnQgKCBcaGF0e1xiZXRhfSBccmlnaHQgKX1eey0xfSBcaGF0e1xiZXRhfSBcc2ltIEZfezEsIG4gLSBwfQokJAoKV2l0aCB0aGUgJFxoYXR7XGJldGF9JCBkZWZpbmUgaW4gdGhlIHRlc3QgJFxsZWZ0ICggXGJlZ2lue21hdHJpeH0KXGhhdHtcYmV0YV97NX19IFxcClxoYXR7XGJldGFfezZ9fQpcZW5ke21hdHJpeH0gXHJpZ2h0ICkkLCB0aGVuICRyPTIkIGFuZCAkbiAtIHAgPSBuIC0gOCQ6CgpgYGB7cn0KZGYgPC0gbnJvdyhtdW5pY2hfcmVudF9pbmRleF8wMSkgLSA4CnIgPC0gMgpiZXRhX2hhdCA8LSBhcy5tYXRyaXgoaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbNjo3XSkKZl81NiA8LSAoMS8yKSAqIHQoYmV0YV9oYXQpICUqJSBzb2x2ZShjb3ZfbWF0cml4WzY6NywgNjo3XSkgJSolIGJldGFfaGF0CmZfNTYKYGBgCgpXZSBnZXQgZXhwZWN0ZWQgJEYkOgoKYGBge3J9CmZfY3JpdCA8LSBxZihwID0gMC45NSwgciwgZGYpCmZfY3JpdApgYGAKCkFuZCB3ZSByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcy4KClRoZSB0aGlyZCBhbmQgZmluYWwgdGVzdCBjb21wYXJpbmcgJFxiZXRhX3s1fSQgYW5kICRcYmV0YV97Nn0kIGJldHdlZW4gZWFjaCBvdGhlci4gV2UgbmVlZDoKCiQkClx3aWRlaGF0e1x0ZXh0e1Zhcn0oXGhhdHtcYmV0YX1fezV9IC0gXGhhdHtcYmV0YX1fezZ9KX0gPSBcd2lkZWhhdHtcdGV4dHtWYXJ9KFxoYXR7XGJldGF9X3s1fSl9ICsgXHdpZGVoYXR7XHRleHR7VmFyfShcaGF0e1xiZXRhfV97Nn0pfSAtIDIgXGNkb3QgXHdpZGVoYXR7XHRleHR7Q292fShcaGF0e1xiZXRhfV97NX0sIFxoYXR7XGJldGF9X3s2fSl9CiQkCgpVc2luZyB0aGUgJFx3aWRlaGF0e1x0ZXh0e0Nvdn0oXGhhdHtcYmV0YX0pfSQgbWF0cml4OgoKYGBge3J9CmNvdl81XzYgPC0gY292X21hdHJpeFs2LCA3XQp2YXJfNSA8LSBjb3ZfbWF0cml4WzYsIDZdCnZhcl82IDwtIGNvdl9tYXRyaXhbNywgN10KdmFyXzVfNiA8LSB2YXJfNSArIHZhcl82IC0gMiAqIGNvdl81XzYKZl81XzYgPC0gKGh5cG90aGVzaXNfbW9kZWwkY29lZmZpY2llbnRzWzZdIC0gaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbN10pXjIgLyB2YXJfNV82CmZfNV82CmBgYAoKQW5kIHRoaXMgJEYkIGNyaXRpY2FsLCB1c2luZyAkcj0xJDoKCmBgYHtyfQpmX2NyaXQgPC0gcWYocCA9IDAuOTUsIDEsIGRmKQpmX2NyaXQKYGBgCgpTbywgaW4gdGhpcyBjYXNlLCB3ZSBkbyBub3QgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMuCgojIyBFeGFtcGxlIDMuMTQgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIENvbmZpZGVuY2UgSW50ZXJ2YWxzCgpUaGUgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgJFxiZXRhX3s3fSQgaXMgb2J0YWluIHVzaW5nOgoKJCQKXGJldGFfezd9IFxwbSB0X3tuIC0gcH0gXGxlZnQgKDEgLSBcYWxwaGEgLyAyIFxyaWdodCApIFxjZG90IFx3aWRlaGF0e1x0ZXh0e1Zhcn0oXGhhdHtcYmV0YX1fezd9KX1eezEvMn0KJCQKCmBgYHtyfQppbnRlciA8LSBxdCgxIC0gMC4wNSAvIDIsIGRmKSAqIHNxcnQodmFyXzcpCmMoaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbOF0gLSBpbnRlciwgaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbOF0gKyBpbnRlcikKYGBgCgojIyBFeGFtcGxlIDMuMTUgTXVuaWNoIFJlbnQgSW5kZXgg4oCUIFByZWRpY3Rpb24gSW50ZXJ2YWxzCgpVc2luZyB0aGUgcHJlZGljdGlvbiBpbnRlcnZhbDoKCiQkCnt4X3swfX1ee1R9IFxjZG90IFxoYXR7XGJldGF9IFxwbSB0X3tuLXB9KDEgLSBcYWxwaGEgLyAyKSBcaGF0e1xzaWdtYX0gXGxlZnQgKCAxICsge3hfezB9fV57VH0gXGxlZnQgKFhee1R9IFggXHJpZ2h0ICleezF9IHhfezB9IFxyaWdodCApXnsxLzJ9CiQkCgpXZSBjYW4gb2J0YWluIHRoZSAkXGhhdHtcc2lnbWF9JCBkaXJlY3RseSBmcm9tIHRoZSBtb2RlbCBvciB1c2luZzoKCiQkCihuIC0gcCkgXGhhdHtcc2lnbWF9XjIgPSBcZXBzaWxvbl57VH0gXGVwc2lsb24KJCQKCmBgYHtyfQpzaWdtYV9tb2RlbCA8LSBzdW1tYXJ5KGh5cG90aGVzaXNfbW9kZWwpJHNpZ21hCnNpZ21hXzIgPC0gdChhcy5tYXRyaXgoaHlwb3RoZXNpc19tb2RlbCRyZXNpZHVhbHMpKSAlKiUgYXMubWF0cml4KGh5cG90aGVzaXNfbW9kZWwkcmVzaWR1YWxzKSAvIChucm93KG11bmljaF9yZW50X2luZGV4XzAxKSAtIDgpCnNpZ21hIDwtIHNxcnQoc2lnbWFfMikKYyhzaWdtYV9tb2RlbCwgc2lnbWEpCmBgYAoKV2UgYXJlIGFsc28gZ29pbmcgdG8gdXNlIHRoZSBkZXNpZ24gbWF0cml4OgoKYGBge3J9CmRlc2lnbl9tYXRyaXggPC0gYXMubWF0cml4KGh5cG90aGVzaXNfbW9kZWwkbW9kZWwpCmRlc2lnbl9tYXRyaXhbLCAxXSA8LSAxCmNvbG5hbWVzKGRlc2lnbl9tYXRyaXgpWzFdIDwtICIxIgpoZWFkKGRlc2lnbl9tYXRyaXgpCmBgYAoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD02LCBmaWcuYWxpZ249J2NlbnRlcid9CmFyZWFfc2VxIDwtIHNlcShtaW4obXVuaWNoX3JlbnRfaW5kZXhfMDEkYXJlYSksIG1heChtdW5pY2hfcmVudF9pbmRleF8wMSRhcmVhKSwgbGVuZ3RoLm91dCA9IDEwMCkKYXJlYWludmMgPC0gKDEgLyBhcmVhX3NlcSkgLSBtZWFuKDEgLyBhcmVhX3NlcSkKCmJldGFtIDwtIGh5cG90aGVzaXNfbW9kZWwkY29lZmZpY2llbnRzCnllYXJjYyA8LSBtdW5pY2hfcmVudF9pbmRleF8wMVttdW5pY2hfcmVudF9pbmRleF8wMSR5ZWFyYyA9PSAxOTE4LCBdWzEsIF0KIyBUaHJlZSBsYXN0IHRlcm1zIHdlcmUgYWRkZWQgZm9yIGNvbXBsZXRlbmVzcwpjb25zdGFudCA8LSBiZXRhbVsxXSArIGJldGFtWzNdICogeWVhcmNjJHllYXJjbyArIGJldGFtWzRdICogeWVhcmNjJHllYXJjbzIgKyBiZXRhbVs1XSAqIHllYXJjYyR5ZWFyY28zICsgYmV0YW1bNl0gKiAwICsgYmV0YW1bN10gKiAwICsgYmV0YW1bOF0gKiAwCgpwbG90KAogIG11bmljaF9yZW50X2luZGV4XzAxJGFyZWEsCiAgbXVuaWNoX3JlbnRfaW5kZXhfMDEkcmVudHNxbV9ldXJvLAogIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogIHlsYWIgPSAnZXN0aW1hdGVkIHJlbnQgcGVyIHNxbScKKQpiZXRhXzEgPC0gaHlwb3RoZXNpc19tb2RlbCRjb2VmZmljaWVudHNbMl0KbGluZXMoYXJlYV9zZXEsIGNvbnN0YW50ICsgYmV0YV8xICogYXJlYWludmMsIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKY29uZl9pbnRlciA8LSBxdCgxIC0gMC4wNSAvIDIsIGRmKSAqIHNxcnQoY292X21hdHJpeFsyLCAyXSkKYmV0YV9jb25mIDwtIGMoYmV0YV8xIC0gY29uZl9pbnRlciwgYmV0YV8xICsgY29uZl9pbnRlcikKbGluZXMoYXJlYV9zZXEsIGNvbnN0YW50ICsgYmV0YV9jb25mWzFdICogYXJlYWludmMsIGNvbCA9ICdkYXJrYmx1ZScsIGx3ZCA9IDIpCmxpbmVzKGFyZWFfc2VxLCBjb25zdGFudCArIGJldGFfY29uZlsyXSAqIGFyZWFpbnZjLCBjb2wgPSAnZGFya2JsdWUnLCBsd2QgPSAyKQoKeF9vIDwtIGFzLm1hdHJpeChjYmluZCgKICAxLAogIGFyZWFpbnZjLAogIHllYXJjYyR5ZWFyY28sCiAgeWVhcmNjJHllYXJjbzIsCiAgeWVhcmNjJHllYXJjbzMsCiAgMCwKICAwLAogIDAKKSkKCnByZWRfaW50ZXIgPC0gTlVMTAoKZm9yIChpIGluIHNlcV9sZW4obnJvdyh4X28pKSkKICB4X29faSA8LSBhcy5tYXRyaXgoeF9vW2ksIF0pCiAgcHJlZF9pbnRlciA8LSBjKHByZWRfaW50ZXIsIHF0KDEgLSAwLjA1IC8gMiwgZGYpICogc2lnbWEgKiBzcXJ0KAogICAgMSArICgKICAgICAgdCh4X29faSkgJSolIHNvbHZlKHQoZGVzaWduX21hdHJpeCklKiVkZXNpZ25fbWF0cml4KSAlKiUgeF9vX2kKICAgKSkKKQoKbGluZXMoYXJlYV9zZXEsIGNvbnN0YW50ICsgYmV0YV9jb25mWzFdICogYXJlYWludmMgLSBwcmVkX2ludGVyLCBjb2wgPSAnZ3JheScsIGx3ZCA9IDIpCmxpbmVzKGFyZWFfc2VxLCBjb25zdGFudCArIGJldGFfY29uZlsyXSAqIGFyZWFpbnZjICsgcHJlZF9pbnRlciwgY29sID0gJ2dyYXknLCBsd2QgPSAyKQpgYGAKClRoaXMgY2FuIGFsc28gYmUgZG9uZSB1c2luZyBgcHJlZGljdGA6CgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTYsIGZpZy5hbGlnbj0nY2VudGVyJ30KeF9vIDwtIGFzLmRhdGEuZnJhbWUoY2JpbmQoCiAgYXJlYWludmMsCiAgeWVhcmNjJHllYXJjbywKICB5ZWFyY2MkeWVhcmNvMiwKICB5ZWFyY2MkeWVhcmNvMywKICAwLAogIDAsCiAgMAopKQpjb2xuYW1lcyh4X28pIDwtIGNvbG5hbWVzKGh5cG90aGVzaXNfbW9kZWwkbW9kZWwpWzI6OF0KY29uZmlkZW5jZV9pbnRlcnZhbCA8LSBwcmVkaWN0KGh5cG90aGVzaXNfbW9kZWwsIHhfbywgaW50ZXJ2YWwgPSAiY29uZmlkZW5jZSIpCgpwbG90KAogICAgbXVuaWNoX3JlbnRfaW5kZXhfMDEkYXJlYSwKICAgIG11bmljaF9yZW50X2luZGV4XzAxJHJlbnRzcW1fZXVybywKICAgIHhsYWIgPSAnYXJlYSBpbiBzcW0nLAogICAgeWxhYiA9ICdlc3RpbWF0ZWQgcmVudCBwZXIgc3FtJwopCmxpbmVzKGFyZWFfc2VxLCBjb25maWRlbmNlX2ludGVydmFsWywgImZpdCJdLCBjb2wgPSAncmVkJywgbHdkID0gMikKbGluZXMoYXJlYV9zZXEsIGNvbmZpZGVuY2VfaW50ZXJ2YWxbLCAibHdyIl0sIGNvbCA9ICdkYXJrYmx1ZScsIGx3ZCA9IDIpCmxpbmVzKGFyZWFfc2VxLCBjb25maWRlbmNlX2ludGVydmFsWywgInVwciJdLCBjb2wgPSAnZGFya2JsdWUnLCBsd2QgPSAyKQoKcHJlZGljdGlvbl9pbnRlcnZhbCA8LSBwcmVkaWN0KGh5cG90aGVzaXNfbW9kZWwsIHhfbywgaW50ZXJ2YWwgPSAicHJlZGljdGlvbiIpCmxpbmVzKGFyZWFfc2VxLCBwcmVkaWN0aW9uX2ludGVydmFsWywgImx3ciJdLCBjb2wgPSAnZ3JheScsIGx3ZCA9IDIpCmxpbmVzKGFyZWFfc2VxLCBwcmVkaWN0aW9uX2ludGVydmFsWywgInVwciJdLCBjb2wgPSAnZ3JheScsIGx3ZCA9IDIpCmBgYAoKIyMgRXhhbXBsZSAzLjE2IFBvbHlub21pYWwgUmVncmVzc2lvbgoKU2ltdWxhdGVkIGRhdGEgZnJvbSB0aGUgcmVhbCBtb2RlbDoKCiQkCnlfe2l9ID0gLTEgKyAwLjMgeF97aX0gKyAwLjQge3hfe2l9fV57Mn0gLSAwLjgge3hfe2l9fV57M30gKyBcZXBzaWxvbl97aX0gXHRleHR7IHdpdGggfSBcZXBzaWxvbl97aX0gXHNpbSBOKDAsIHswLjA3fV57Mn0pCiQkCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEyLCBmaWcuYWxpZ249J2NlbnRlcid9CnBhcihtZnJvdyA9IGMoMywgMikpCgp4X3NlcSA8LSBzZXEoMCwgMSwgbGVuZ3RoLm91dCA9IDUwKQp0cmFpbmluZ19kYXRhIDwtIGRhdGEuZnJhbWUoCiAgeCA9IHhfc2VxLAogIHkgPSAtMSArIDAuMyAqIHhfc2VxICsgMC40ICogeF9zZXEgXiAyIC0gMC44ICogeF9zZXEgXiAzICsgcm5vcm0oNTAsIDAsIDAuMDcpCikKdmFsaWRhdGlvbl9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgeCA9IHhfc2VxLAogIHkgPSAtMSArIDAuMyAqIHhfc2VxICsgMC40ICogeF9zZXEgXiAyIC0gMC44ICogeF9zZXEgXiAzICsgcm5vcm0oNTAsIDAsIDAuMDcpCikKCnBsb3RfdHJhaW5pbmdfZGF0YSA8LSBmdW5jdGlvbih0aXRsZSkgewogIHBsb3QoCiAgICB0cmFpbmluZ19kYXRhLAogICAgbWFpbiA9IHRpdGxlLAogICAgeGxhYiA9ICd4JywKICAgIHlsYWIgPSAneScKICApCn0KCnBsb3RfdHJhaW5pbmdfZGF0YSgndHJhaW5pbmdfZGF0YScpCnBsb3QoCiAgdmFsaWRhdGlvbl9kYXRhLAogIG1haW4gPSAndmFsaWRhdGlvbl9kYXRhJywKICB4bGFiID0gJ3gnLAogIHlsYWIgPSAneScKKQoKYWRqdXN0X3BvbHlub21pYWwgPC0gZnVuY3Rpb24obCkgewogIG1vZGVsIDwtICJ5IH4geCIKICBpZiAobCAhPSAxKSB7CiAgICBmb3IgKGkgaW4gMjpsKXsKICAgICAgbW9kZWwgPC0gcGFzdGUwKG1vZGVsLCAiICsgSSh4XiIsIGksICIpIikKICAgIH0KICB9CiAgbG0obW9kZWwsIGRhdGEgPSB0cmFpbmluZ19kYXRhKQp9Cgpwb2x5bm9taWFsX21vZGVscyA8LSBsYXBwbHkoMTo5LCBhZGp1c3RfcG9seW5vbWlhbCkKcGxvdF90cmFpbmluZ19kYXRhKCdyZWdyZXNzaW9uIGxpbmUnKQpsaW5lcyh4X3NlcSwgcHJlZGljdChwb2x5bm9taWFsX21vZGVsc1sxXVtbMV1dKSwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCgpwbG90X3RyYWluaW5nX2RhdGEoJ3BvbHlub21pYWwgcmVncmVzc2lvbiB3aXRoIGw9MicpCmxpbmVzKHhfc2VxLCBwcmVkaWN0KHBvbHlub21pYWxfbW9kZWxzWzJdW1sxXV0pLCBjb2wgPSAncmVkJywgbHdkID0gMikKCnBsb3RfdHJhaW5pbmdfZGF0YSgncG9seW5vbWlhbCByZWdyZXNzaW9uIHdpdGggbD01JykKbGluZXMoeF9zZXEsIHByZWRpY3QocG9seW5vbWlhbF9tb2RlbHNbNV1bWzFdXSksIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQoKbWVhbl9zcXVhcmVfZXJyb3IgPC0gZnVuY3Rpb24obW9kZWwsIGRhdGEpewogICgxIC8gNTApICogc3VtKChkYXRhJHkgLSBwcmVkaWN0KG1vZGVsLCBuZXdfZGF0YSA9IGRhdGEpKSBeIDIpCn0KCm1zZV90cmFpbmluZ19kYXRhIDwtIHVubGlzdChsYXBwbHkocG9seW5vbWlhbF9tb2RlbHMsIG1lYW5fc3F1YXJlX2Vycm9yLCBkYXRhID0gdHJhaW5pbmdfZGF0YSkpCm1zZV92YWxpZGF0aW9uX2RhdGEgPC0gdW5saXN0KGxhcHBseShwb2x5bm9taWFsX21vZGVscywgbWVhbl9zcXVhcmVfZXJyb3IsIGRhdGEgPSB2YWxpZGF0aW9uX2RhdGEpKQoKcGxvdCgKICBzZXEoMSwgOSwgMSksCiAgbXNlX3RyYWluaW5nX2RhdGEsCiAgeWxpbSA9IGMobWluKG1zZV90cmFpbmluZ19kYXRhLCBtc2VfdmFsaWRhdGlvbl9kYXRhKSwgbWF4KG1zZV90cmFpbmluZ19kYXRhLCBtc2VfdmFsaWRhdGlvbl9kYXRhKSksCiAgbWFpbiA9ICdNU0UgZm9yIHRyYWluaW5nIGFuZCB2YWxpZGF0aW9uIGRhdGEnLAogIHhsYWIgPSAnZGVncmVlIG9mIHBvbHlub21pYWwnLAogIHlsYWIgPSAnTVNFJywKICB0eXBlID0gJ2wnLAogIGNvbCA9ICdkYXJrYmx1ZScsCiAgbHdkID0gMgopCmxpbmVzKHNlcSgxLCA5LCAxKSwgbXNlX3ZhbGlkYXRpb25fZGF0YSwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCmBgYAoKIyMgRXhhbXBsZSAzLjE3IENvcnJlbGF0ZWQgQ292YXJpYXRlcwoKU2ltdWxhdGUgdGhlIGRhdGEgYXMgZm9sbG93aW5nOgoKJCQKeF97MX0gXHNpbSBVKDAsIDEpIFx3ZWRnZSB4X3szfSBcc2ltIFUoMCwgMSkKJCQKCiQkCnhfezJ9ID0geF97MX0gKyB1IFx0ZXh0eyB3aXRoIH0gdSBcc2ltIFUoMCwgMSkKJCQKCiQkCnkgXHNpbSBOKC0xICsgMC4zIHhfezF9ICsgMC4yIHhfezN9LCB7MC4yfV57Mn0pCiQkCgpgYGB7ciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9OCwgZmlnLmFsaWduPSdjZW50ZXInfQpuIDwtIDE1MAoKeDEgPC0gcnVuaWYobiwgMCwgMSkKeDMgPC0gcnVuaWYobiwgMCwgMSkKdSA8LSBydW5pZihuLCAwLCAxKQoKeDIgPC0geDEgKyB1Cgp5IDwtIHJub3JtKG4sIG1lYW4gPSAtMSArIDAuMyAqIHgxICsgMC4yICogeDMsIDAuMikKCmNvcnJfY292X2RhdGEgPC0gZGF0YS5mcmFtZSgKICB5ID0geSwKICB4MSA9IHgxLAogIHgyID0geDIsCiAgeDMgPSB4MwopCnBhaXJzKGNvcnJfY292X2RhdGEpCmBgYAoKQWRqdXN0IHRoZSBtb2RlbCB3aXRoICR4X3sxfSQsICR4X3syfSQgYW5kICR4X3szfSQ6CgpgYGB7cn0Kd3JvbmdfbW9kZWwgPC0gbG0oeSB+IHgxICsgeDIgKyB4MywgZGF0YSA9IGNvcnJfY292X2RhdGEpCnN1bW1hcnkod3JvbmdfbW9kZWwpCmBgYAoKQWRqdXN0IHRoZSBtb2RlbCB3aXRoICR4X3sxfSQgYW5kICR4X3szfSQ6CgpgYGB7cn0KY29ycmVjdF9tb2RlbCA8LSBsbSh5IH4geDEgKyB4MywgZGF0YSA9IGNvcnJfY292X2RhdGEpCnN1bW1hcnkoY29ycmVjdF9tb2RlbCkKYGBgCgojIyBFeGFtcGxlIDMuMTggUG9seW5vbWlhbCBSZWdyZXNzaW9uIOKAlCBNb2RlbCBDaG9pY2Ugd2l0aCBBSUMKCldlIGNhbGN1bGF0ZSBBSUMgdXNpbmc6CgokJApBSUMgPSBuIFxjZG90IFxsb2coXGhhdHtcc2lnbWF9XnsyfSkgKyAgMiBcbGVmdCAoIFxsZWZ0IHwgTSBccmlnaHQgfCArIDEgXHJpZ2h0ICkKJCQKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00LCBmaWcuYWxpZ249J2NlbnRlcid9CmFpYyA8LSBmdW5jdGlvbihtb2RlbCl7CiAgbSA8LSBuY29sKG1vZGVsJG1vZGVsKSAtIDEKICBuIDwtIG5yb3cobW9kZWwkbW9kZWwpCiAgbiAqIGxvZyhzdW1tYXJ5KG1vZGVsKSRzaWdtYV4yKSArIDIgKiAobSArIDEpCn0KCmFpY192YWx1ZXMgPC0gdW5saXN0KGxhcHBseShwb2x5bm9taWFsX21vZGVscywgYWljKSkKcGxvdCgKICBhaWNfdmFsdWVzLAogIG1haW4gPSAnQUlDIGFzIGEgZnVuY3Rpb24gb2YgZGVncmVlIG9mIHBvbHlub21pYWxzJywKICB4bGFiID0gJ2RlZ3JlZXMgb2YgcG9seW5vbWlhbCcsCiAgeWxhYiA9ICdhaWMnLAogIHR5cGUgPSAnbCcsCiAgY29sID0gJ2RhcmtibHVlJywKICBsd2QgPSAyCikKYGBgCgpBcyBhbHdheXMsIHRoaXMgY2FuIGFsc28gYmUgZG9uZSB1c2luZyBSOgoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0nY2VudGVyJ30KYWljIDwtIGZ1bmN0aW9uKG1vZGVsKXsKICBBSUMobW9kZWwpCn0KCmFpY192YWx1ZXMgPC0gdW5saXN0KGxhcHBseShwb2x5bm9taWFsX21vZGVscywgYWljKSkKcGxvdCgKICAgICAgICBhaWNfdmFsdWVzLAogICAgICAgIG1haW4gPSAnQUlDIGFzIGEgZnVuY3Rpb24gb2YgZGVncmVlIG9mIHBvbHlub21pYWxzJywKICAgICAgICB4bGFiID0gJ2RlZ3JlZXMgb2YgcG9seW5vbWlhbCcsCiAgICAgICAgeWxhYiA9ICdhaWMnLAogICAgICAgIHR5cGUgPSAnbCcsCiAgICAgICAgY29sID0gJ2RhcmtibHVlJywKICAgICAgICBsd2QgPSAyCikKYGBgCgojIyBFeGFtcGxlIDMuMTkgUHJpY2VzIG9mIFVzZWQgQ2FycyDigJQgTW9kZWwgQ2hvaWNlCgpBcyBmb3IgYWxsIGRhdGFzZXQsIHdlIGltcG9ydCB0aGUgXyJwcmljZXMgb2YgdXNlZCBjYXJzIl8gZGF0YXNldCBmcm9tIHRoZSBbb2ZmaWNpYWwgcGFnZSBvZiB0aGUgZGF0YXNldF0oaHR0cHM6Ly93d3cudW5pLWdvZXR0aW5nZW4uZGUvZW4vNTUxNjI1Lmh0bWwpOgoKYGBge3J9CmNhcnNfdXJsIDwtICJodHRwczovL3d3dy51bmktZ29ldHRpbmdlbi5kZS9kZS9kb2N1bWVudC9kb3dubG9hZC8wNjJjYWRmZGExYzRjMjk1Zjk0NjBiNDljN2Y1Nzk5ZS5yYXcvZ29sZmZ1bGwucmF3IgoKdXNlZF9jYXJzIDwtIHJlYWQudGFibGUoCiAgdXJsKGNhcnNfdXJsKSwKICBoZWFkZXIgPSAxLAogIGNvbENsYXNzZXMgPSBjKAogICAgIm51bWVyaWMiLCAiaW50ZWdlciIsICJudW1lcmljIiwKICAgICJpbnRlZ2VyIiwgImZhY3RvciIsICJmYWN0b3IiLAogICAgIm51bWVyaWMiLCAibnVtZXJpYyIsICJudW1lcmljIiwKICAgICJudW1lcmljIiwgIm51bWVyaWMiLCAibnVtZXJpYyIKICApCikKCiMgQ29udmVydCB0byBsb2dpY2FsCnVzZWRfY2FycyRleHRyYXMxIDwtIHVzZWRfY2FycyRleHRyYXMxID09IDEKdXNlZF9jYXJzJGV4dHJhczIgPC0gdXNlZF9jYXJzJGV4dHJhczIgPT0gMQoKIyBSZWNhbGN1bGF0ZSBwb2x5bm9taWFscwpwb2x5X2tpbG9tZXRlciA8LSBwb2x5KHVzZWRfY2FycyRraWxvbWV0ZXIsIDMpCnVzZWRfY2Fyc1ssIGMoImtpbG9tZXRlcm9wMSIsICJraWxvbWV0ZXJvcDIiLCAia2lsb21ldGVyb3AzIildIDwtIHBvbHlfa2lsb21ldGVyCnBvbHlfYWdlIDwtIHBvbHkodXNlZF9jYXJzJGFnZSwgMykKdXNlZF9jYXJzWywgYygiYWdlb3AxIiwgImFnZW9wMiIsICJhZ2VvcDMiKV0gPC0gcG9seV9hZ2UKc3VtbWFyeSh1c2VkX2NhcnMpCmBgYAoKRXhwbG9yaW5nIHRoZSBkYXRhOgoKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMiwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDMsIDIpKQoKcGxvdCgKICB1c2VkX2NhcnMkYWdlLAogIHVzZWRfY2FycyRwcmljZSwKICBtYWluID0gJ1NjYXR0ZXIgcGxvdDogc2FsZXMgcHJpY2UgdmVyc3VzIGFnZSBpbiBtb250aHMnLAogIHhsYWIgPSAnYWdlIGluIG1vbnRocycsCiAgeWxhYiA9ICdzYWxlcyBwcmluY2UgaW4gMTAwMCBFdXJvJwopCgpwbG90KAogIHVzZWRfY2FycyRraWxvbWV0ZXIsCiAgdXNlZF9jYXJzJHByaWNlLAogIG1haW4gPSAnU2NhdHRlciBwbG90OiBzYWxlcyBwcmljZSB2ZXJzdXMga2lsb21ldGVyIHJlYWRpbmcnLAogIHhsYWIgPSAna2lsb21ldGVyIHJlYWRpbmcgaW4gMTAwMCBrbScsCiAgeWxhYiA9ICdzYWxlcyBwcmluY2UgaW4gMTAwMCBFdXJvJwopCgpwbG90KAogIHVzZWRfY2FycyRUSUEsCiAgdXNlZF9jYXJzJHByaWNlLAogIG1haW4gPSAnU2NhdHRlciBwbG90OiBzYWxlcyBwcmljZSB2ZXJzdXMgbW9udGhzIHVudGlsIFRJQScsCiAgeGxhYiA9ICdtb250aHMgdW50aWwgbmV4dCBUSUEgYXBwb2ludG1lbnQnLAogIHlsYWIgPSAnc2FsZXMgcHJpbmNlIGluIDEwMDAgRXVybycKKQoKYm94cGxvdCgKICBwcmljZSB+IGV4dHJhczEsCiAgZGF0YSA9IHVzZWRfY2FycywKICBtYWluID0gJ0JveCBwbG90OiBzYWxlcyBwcmluY2Ugd2l0aCBvciB3aXRob3V0IEFCUycsCiAgeGxhYiA9ICcnLAogIHlsYWIgPSAnc2FsZXMgcHJpY2VzIGluIDEwMDAgRXVybycsCiAgbmFtZXMgPSBjKCdubyBBQlMnLCAnQUJTJykKKQoKYm94cGxvdCgKICBwcmljZSB+IGV4dHJhczIsCiAgZGF0YSA9IHVzZWRfY2FycywKICBtYWluID0gJ0JveCBwbG90OiBzYWxlcyBwcmluY2Ugd2l0aCBvciB3aXRob3V0IHN1bnJvb2YnLAogIHhsYWIgPSAnJywKICB5bGFiID0gJ3NhbGVzIHByaWNlcyBpbiAxMDAwIEV1cm8nLAogIG5hbWVzID0gYygnbm8gc3Vucm9vZicsICdzdW5yb29mJykKKQpgYGAKCldlIGV4cGxvcmUgdGhlIG1vZGVscyBzdWdnZXN0ZWQ6CgoqKiRNMSQqKgoKJCQKXGJlZ2lue21hdHJpeH0KICBcbWF0aGJie0V9KFx0ZXh0e3ByaWNlfSkgPSAmIFxiZXRhX3swfSArIFxiZXRhX3sxfSBcdGV4dHtraWxvbWV0ZXJvcDF9ICsgXGJldGFfezJ9IFx0ZXh0e2tpbG9tZXRlcm9wMn0gKyBcYmV0YV97M30gXHRleHR7a2lsb21ldGVyb3AzfSBcXAogICAmICsgXGJldGFfezR9IFx0ZXh0e2FnZW9wMX0gKyBcYmV0YV97NX0gXHRleHR7YWdlb3AyfSArIFxiZXRhX3s2fSBcdGV4dHthZ2VvcDN9ClxlbmR7bWF0cml4fQokJAoKV2l0aCAkXHRleHR7a2lsb21ldGVyb3B9aSQgdGhlICRpJCB0ZXJtIG9mIGEgcG9seW5vbWlhbCBvZiBkZWdyZWUgMyBvZiB0aGUgJFx0ZXh0e2tpbG9tZXRlcn0kIHZhcmlhYmxlLiBFcXVpdmFsZW50IHRvICRcdGV4dHthZ2VvcH1pJC4KCioqJE0yJCoqCgokJApcYmVnaW57bWF0cml4fQogIFxtYXRoYmJ7RX0oXHRleHR7cHJpY2V9KSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHtraWxvbWV0ZXJvcDN9IFxcCiAgICYgKyBcYmV0YV97NH0gXHRleHR7YWdlb3AxfSArIFxiZXRhX3s1fSBcdGV4dHthZ2VvcDJ9ICsgXGJldGFfezZ9IFx0ZXh0e2FnZW9wM30gXFwKICAgJiArIFxiZXRhX3s3fSBcdGV4dHtleHRyYXMxfQpcZW5ke21hdHJpeH0KJCQKCioqJE0zJCoqCgokJApcYmVnaW57bWF0cml4fQogIFxtYXRoYmJ7RX0oXHRleHR7cHJpY2V9KSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHtraWxvbWV0ZXJvcDN9IFxcCiAgICYgKyBcYmV0YV97NH0gXHRleHR7YWdlb3AxfSArIFxiZXRhX3s1fSBcdGV4dHthZ2VvcDJ9ICsgXGJldGFfezZ9IFx0ZXh0e2FnZW9wM30gXFwKICAgJiArIFxiZXRhX3s3fSBcdGV4dHtleHRyYXMyfQpcZW5ke21hdHJpeH0KJCQKCioqJE00JCoqCgokJApcYmVnaW57bWF0cml4fQogIFxtYXRoYmJ7RX0oXHRleHR7cHJpY2V9KSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHtraWxvbWV0ZXJvcDN9IFxcCiAgICYgKyBcYmV0YV97NH0gXHRleHR7YWdlb3AxfSArIFxiZXRhX3s1fSBcdGV4dHthZ2VvcDJ9ICsgXGJldGFfezZ9IFx0ZXh0e2FnZW9wM30gXFwKICAgJiArIFxiZXRhX3s3fSBcdGV4dHtUSUF9ClxlbmR7bWF0cml4fQokJAoKKiokTTUkKioKCiQkClxiZWdpbnttYXRyaXh9CiAgXG1hdGhiYntFfShcdGV4dHtwcmljZX0pID0gJiBcYmV0YV97MH0gKyBcYmV0YV97MX0gXHRleHR7a2lsb21ldGVyb3AxfSArIFxiZXRhX3syfSBcdGV4dHtraWxvbWV0ZXJvcDJ9ICsgXGJldGFfezN9IFx0ZXh0e2tpbG9tZXRlcm9wM30gXFwKICAgJiArIFxiZXRhX3s0fSBcdGV4dHthZ2VvcDF9ICsgXGJldGFfezV9IFx0ZXh0e2FnZW9wMn0gKyBcYmV0YV97Nn0gXHRleHR7YWdlb3AzfSBcXAogICAmICsgXGJldGFfezd9IFx0ZXh0e2V4dHJhczF9ICsgXGJldGFfezh9IFx0ZXh0e2V4dHJhczJ9ClxlbmR7bWF0cml4fQokJAoKKiokTTYkKioKCiQkClxiZWdpbnttYXRyaXh9CiAgXG1hdGhiYntFfShcdGV4dHtwcmljZX0pID0gJiBcYmV0YV97MH0gKyBcYmV0YV97MX0gXHRleHR7a2lsb21ldGVyb3AxfSArIFxiZXRhX3syfSBcdGV4dHtraWxvbWV0ZXJvcDJ9ICsgXGJldGFfezN9IFx0ZXh0e2tpbG9tZXRlcm9wM30gXFwKICAgJiArIFxiZXRhX3s0fSBcdGV4dHthZ2VvcDF9ICsgXGJldGFfezV9IFx0ZXh0e2FnZW9wMn0gKyBcYmV0YV97Nn0gXHRleHR7YWdlb3AzfSBcXAogICAmICsgXGJldGFfezd9IFx0ZXh0e2V4dHJhczF9ICsgXGJldGFfezh9IFx0ZXh0e1RJQX0KXGVuZHttYXRyaXh9CiQkCgoqKiRNNyQqKgoKJCQKXGJlZ2lue21hdHJpeH0KICBcbWF0aGJie0V9KFx0ZXh0e3ByaWNlfSkgPSAmIFxiZXRhX3swfSArIFxiZXRhX3sxfSBcdGV4dHtraWxvbWV0ZXJvcDF9ICsgXGJldGFfezJ9IFx0ZXh0e2tpbG9tZXRlcm9wMn0gKyBcYmV0YV97M30gXHRleHR7a2lsb21ldGVyb3AzfSBcXAogICAmICsgXGJldGFfezR9IFx0ZXh0e2FnZW9wMX0gKyBcYmV0YV97NX0gXHRleHR7YWdlb3AyfSArIFxiZXRhX3s2fSBcdGV4dHthZ2VvcDN9IFxcCiAgICYgKyBcYmV0YV97N30gXHRleHR7ZXh0cmFzMn0gKyBcYmV0YV97OH0gXHRleHR7VElBfQpcZW5ke21hdHJpeH0KJCQKCioqJE04JCoqCgokJApcYmVnaW57bWF0cml4fQogIFxtYXRoYmJ7RX0oXHRleHR7cHJpY2V9KSA9ICYgXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHtraWxvbWV0ZXJvcDN9IFxcCiAgICYgKyBcYmV0YV97NH0gXHRleHR7YWdlb3AxfSArIFxiZXRhX3s1fSBcdGV4dHthZ2VvcDJ9ICsgXGJldGFfezZ9IFx0ZXh0e2FnZW9wM30gXFwKICAgJiArIFxiZXRhX3s3fSBcdGV4dHtleHRyYXMxfSArIFxiZXRhX3s4fSBcdGV4dHtleHRyYXMyfSArIFxiZXRhX3s5fSBcdGV4dHtUSUF9ClxlbmR7bWF0cml4fQokJAoKCmBgYHtyfQpiYXNlX2Nhcl9tb2RlbCA8LSAicHJpY2UgfiBraWxvbWV0ZXJvcDEgKyBraWxvbWV0ZXJvcDIgKyBraWxvbWV0ZXJvcDMgKyBhZ2VvcDEgKyBhZ2VvcDIgKyBhZ2VvcDMiCnVzZWRfY2Fyc19tb2RlbCA8LSBsaXN0KGxtKGJhc2VfY2FyX21vZGVsLCBkYXRhID0gdXNlZF9jYXJzKSkKZXh0cmFfdmFyIDwtIGMoImV4dHJhczEiLCAiZXh0cmFzMiIsICJUSUEiKQpmb3IgKGV4dHJhIGluIGV4dHJhX3Zhcil7CiAgbmV3X21vZGVsIDwtIHBhc3RlMChiYXNlX2Nhcl9tb2RlbCwgIiArICIsIGV4dHJhKQogIHVzZWRfY2Fyc19tb2RlbFtbbGVuZ3RoKHVzZWRfY2Fyc19tb2RlbCkgKyAxXV0gPC0gbG0obmV3X21vZGVsLCBkYXRhID0gdXNlZF9jYXJzKQp9CmZvciAodHVwbGUgaW4gY29tYm4oZXh0cmFfdmFyLCAyLCBzaW1wbGlmeSA9IEZBTFNFKSl7CiAgbmV3X21vZGVsIDwtIHBhc3RlMChiYXNlX2Nhcl9tb2RlbCwgIiArICIsIHR1cGxlWzFdLCAiICsgIiwgdHVwbGVbMl0pCiAgdXNlZF9jYXJzX21vZGVsW1tsZW5ndGgodXNlZF9jYXJzX21vZGVsKSArIDFdXSA8LSBsbShuZXdfbW9kZWwsIGRhdGEgPSB1c2VkX2NhcnMpCn0KYWxsX2V4dHJhcyA8LSBwYXN0ZShleHRyYV92YXIsIGNvbGxhcHNlID0gIiArICIpCnVzZWRfY2Fyc19tb2RlbFtbbGVuZ3RoKHVzZWRfY2Fyc19tb2RlbCkgKyAxXV0gPC0gbG0ocGFzdGUwKGJhc2VfY2FyX21vZGVsLCAiICsgIiwgYWxsX2V4dHJhcyksIGRhdGEgPSB1c2VkX2NhcnMpCgpjYXJzX2FpYyA8LSBkYXRhLmZyYW1lKG1vZGVsID0gc2VxKDEsIDgpLCBhaWMgPSB1bmxpc3QobGFwcGx5KHVzZWRfY2Fyc19tb2RlbCwgQUlDKSkpCmNhcnNfYWljCmBgYAoKOXRoIG1vZGVsIGFyZSBvYnRhaW5lZCB1c2luZyBfImxlYXBzIGFuZCAgYm91bmRzIl8gYWxnb3JpdGhtLCBhbHJlYWR5IGltcGxlbWVudGVkIG9uIFI6CgpgYGB7cn0KbGlicmFyeShsZWFwcykKCmxlYXBzX21vZGVsIDwtIHJlZ3N1YnNldHMocHJpY2UgfiAuIC1hZ2UgLWtpbG9tZXRlciwgZGF0YSA9IHVzZWRfY2FycywgbnZtYXggPSA0KQoKc3VtbWFyeShsZWFwc19tb2RlbCkKYGBgCgpBcyBpbiB0aGUgYm9vayB0aGUgcmVzdWx0ZWQgbW9kZWwgaXM6CgoqKiRNOSQqKgoKJCQKXG1hdGhiYntFfShcdGV4dHtwcmljZX0pID0gXGJldGFfezB9ICsgXGJldGFfezF9IFx0ZXh0e2tpbG9tZXRlcm9wMX0gKyBcYmV0YV97Mn0gXHRleHR7a2lsb21ldGVyb3AyfSArIFxiZXRhX3szfSBcdGV4dHthZ2VvcDF9ICsgXGJldGFfezR9IFx0ZXh0e2FnZW9wMn0KJCQKCldlIGFkZCBpdCB0byB0aGUgdGFibGUgYW5kIHBsb3QgdGhlIEFJQzoKCmBgYHtyLCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01LCBmaWcuYWxpZ249J2NlbnRlcid9CnNlbGVjdGVkX3ZhciA8LSBuYW1lcyh3aGljaChzdW1tYXJ5KGxlYXBzX21vZGVsKSRvdXRtYXRbNCwgXSA9PSAiKiIpKQp1c2VkX2NhcnNfbW9kZWxbW2xlbmd0aCh1c2VkX2NhcnNfbW9kZWwpICsgMSBdXSA8LSBsbSgKICBwYXN0ZTAoInByaWNlIH4gIiwgcGFzdGUoc2VsZWN0ZWRfdmFyLCBjb2xsYXBzZT0gIiArICIpKSwgZGF0YSA9IHVzZWRfY2FycwopCmNhcnNfYWljWzksIF0gPC0gYyg5LCBBSUModXNlZF9jYXJzX21vZGVsW1s5XV0pKQpjYXJzX2FpYwoKcGxvdCgKICBjYXJzX2FpYywKICBtYWluID0gJ0FJQyBmb3IgdGhlIGRpZmZlcmVudCBtb2RlbHMnLAogIHhsYWIgPSAnbW9kZWwgbnVtYmVyJywKICB5bGFiID0gJ0FJQycsCiAgeGF4dCA9ICduJwopCmF4aXMoMSwgYXQgPSAxOjkpCmBgYAoKU2hvd2luZyB0aGUgYWdlIGFuZCBraWxvbWV0ZXIgZWZmZWN0cyBhcyBpbiBFeGFtcGxlIDMuNS4KCgpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTQsIGZpZy5hbGlnbj0nY2VudGVyJ30KcGFyKG1mcm93ID0gYygxLCAyKSkKCmJldGFfbTkgPC0gdXNlZF9jYXJzX21vZGVsW1s5XV0kY29lZmZpY2llbnRzCgphZ2Vfc2VxIDwtIHNlcShtaW4odXNlZF9jYXJzJGFnZSksIG1heCh1c2VkX2NhcnMkYWdlKSwgbGVuZ3RoLm91dCA9IDEwMCkKa2lsb21ldGVyX3NlcSA8LSBzZXEobWluKHVzZWRfY2FycyRraWxvbWV0ZXIpLCBtYXgodXNlZF9jYXJzJGtpbG9tZXRlciksIGxlbmd0aC5vdXQgPSAxMDApCgphZ2VfZWZmZWN0IDwtIGZ1bmN0aW9uKGFnZSl7CiAgaV9wb2x5X2FnZSA8LSBwcmVkaWN0KHBvbHlfYWdlLCBhZ2UpCiAgYmV0YV9tOVs0XSAqIGlfcG9seV9hZ2VbLCAxXSArIGJldGFfbTlbNV0gKiBpX3BvbHlfYWdlWywgMl0KfQoKa2lsb21ldGVyX2VmZmVjdCA8LSBmdW5jdGlvbihraWxvbWV0ZXIpewogIGlfcG9seV9raWxsb21ldGVyIDwtIHByZWRpY3QocG9seV9raWxvbWV0ZXIsIGtpbG9tZXRlcikKICBiZXRhX205WzJdICogaV9wb2x5X2tpbGxvbWV0ZXJbLCAxXSArIGJldGFfbTlbM10gKiBpX3BvbHlfa2lsbG9tZXRlclssIDJdCn0KCnJlc2lkdWFsX2FnZSA8LSB1c2VkX2NhcnMkcHJpY2UgLSBiZXRhX205WzFdIC0ga2lsb21ldGVyX2VmZmVjdCh1c2VkX2NhcnMka2lsb21ldGVyKQpwbG90KAogIHVzZWRfY2FycyRhZ2UsCiAgcmVzaWR1YWxfYWdlLAogIG1haW4gPSAnZWZmZWN0IG9mIGFnZSBpbmNsdWRpbmcgcGFydGlhbCByZXNpZHVhbHMnLAogIHhsYWIgPSAnYWdlIGluIG1vbnRocycsCiAgeWxhYiA9ICdlZmZlY3QgLyBwYXJ0aWFsIHJlc2lkdWFscycKKQpsaW5lcyhhZ2Vfc2VxLCBhZ2VfZWZmZWN0KGFnZV9zZXEpLCBjb2wgPSAncmVkJywgbHdkID0gMikKCnJlc2lkdWFsX2tpbG9tZXRlciA8LSB1c2VkX2NhcnMkcHJpY2UgLSBiZXRhX205WzFdIC0gYWdlX2VmZmVjdCh1c2VkX2NhcnMkYWdlKQpwbG90KAogIHVzZWRfY2FycyRraWxvbWV0ZXIsCiAgcmVzaWR1YWxfYWdlLAogIG1haW4gPSAnZWZmZWN0IG9mIGtpbG9tZXRlciByZWFkaW5nIGluY2x1ZGluZyBwYXJ0aWFsIHJlc2lkdWFscycsCiAgeGxhYiA9ICdraWxvbWV0ZXIgcmVhZGluZyBpbiAxMDAwa20nLAogIHlsYWIgPSAnZWZmZWN0IC8gcGFydGlhbCByZXNpZHVhbHMnCikKbGluZXMoa2lsb21ldGVyX3NlcSwga2lsb21ldGVyX2VmZmVjdChraWxvbWV0ZXJfc2VxKSwgY29sID0gJ3JlZCcsIGx3ZCA9IDIpCmBgYAoKIyMgRXhhbXBsZSAzLjIwIFByaWNlIG9mIFVzZWQgQ2FycyDigJQgTW9kZWwgRGlhZ25vc2lzCgpUaGUgc3R1ZGVudGl6ZWQgcmVzaWR1YWxzIGFyZSBjYWxjdWxhdGVkIHdpdGggdGhlIGVxdWF0aW9uOgoKJCQKcl97aX1eeyp9ID0gXGZyYWN7XGhhdHtcZXBzaWxvbn1fe2l9fXtcaGF0e1xzaWdtYX1feyhpKX0gXHNxcnR7MSAtIGhfe2lpfX19CiQkCgpCdXQsIHlvdSBjYW4gZ2V0IHRoZW0gdXNpbmcgUjoKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDIsIDIpKQoKdF9yZXNpZHVhbHMgPC0gcnN0dWRlbnQodXNlZF9jYXJzX21vZGVsW1s5XV0pCgpuIDwtIG5yb3codXNlZF9jYXJzX21vZGVsW1s5XV0kbW9kZWwpCnAgPC0gbmNvbCh1c2VkX2NhcnNfbW9kZWxbWzldXSRtb2RlbCkKdF9jcml0aWNhbCA8LSBxdCgxIC0gMC4wNSAvICgyICogbiksIG4gLSBwIC0gMSkKCnBsb3Rfc3R1ZGVudGl6ZWRfcmVzaWR1YWxzIDwtIGZ1bmN0aW9uKGFnYWluc3QsIGFnX2xhYmVsLCBzaG9ydF9sYWJlbCl7CiAgcGxvdCgKICAgIGFnYWluc3QsCiAgICB0X3Jlc2lkdWFscywKICAgIG1haW4gPSBwYXN0ZTAoJ3N0dWRlbnRpemVkIHJlc2lkdWFscyB2ZXJ1cyAnLCBzaG9ydF9sYWJlbCksCiAgICB4bGFiID0gYWdfbGFiZWwsCiAgICB5bGFiID0gJ3N0dWRlbnRpemVkIHJlc2lkdWFscycsCiAgICB5bGltID0gYyhtaW4oYygtdF9jcml0aWNhbCAtIDAuNSwgdF9yZXNpZHVhbHMpKSwgbWF4KGModF9jcml0aWNhbCArIDAuNSwgdF9yZXNpZHVhbHMpKSkKICApCiAgYWJsaW5lKGggPSAwLCBjb2wgPSAncmVkJywgbHdkID0gMikKICBhYmxpbmUoaCA9IGMoLXRfY3JpdGljYWwsIHRfY3JpdGljYWwpLCBjb2wgPSAnZ3JheScsIGx3ZCA9IDIpCn0KcGxvdF9zdHVkZW50aXplZF9yZXNpZHVhbHModXNlZF9jYXJzX21vZGVsW1s5XV0kZml0dGVkLnZhbHVlcywgJ2VzdGltYXRlZCBzYWxlcyBwcmljZScsICdlc3RpbWF0ZWQgc2FsZXMgcHJpY2UnKQpwbG90X3N0dWRlbnRpemVkX3Jlc2lkdWFscyh1c2VkX2NhcnMka2lsb21ldGVyLCAna2lsb21ldGVyIHJlYWRpbmcgaW4gMTAwMGttJywgJ2tpbG9tZXRlciByZWFkaW5nJykKcGxvdF9zdHVkZW50aXplZF9yZXNpZHVhbHModXNlZF9jYXJzJGFnZSwgJ2FnZSBpbiBtb250aHMnLCAnYWdlIGluIG1vbnRocycpCnBsb3Rfc3R1ZGVudGl6ZWRfcmVzaWR1YWxzKHVzZWRfY2FycyRUSUEsICdtb250aHMgdG8gdGhlIG5leHQgVElBIGFwcG9pbnRtZW50JywgJ21vbnRocyB0byBUSUEnKQpgYGAKCkZvciB0aGUgc3RhbmRhcmRpemVkIHJlc2lkdWFsczoKCiQkCnJfe2l9ID0gXGZyYWN7XGhhdHtcZXBzaWxvbn1fe2l9fXtcaGF0e1xzaWdtYX1cc3FydHsxIC0gaF97aWl9fX0KJCQKCkJ1dCB1c2luZyBSOgoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTUsIGZpZy5hbGlnbj0nY2VudGVyJ30Kbl9yZXNpZHVhbHMgPC0gcnN0YW5kYXJkKHVzZWRfY2Fyc19tb2RlbFtbOV1dKQoKcXFub3JtKG5fcmVzaWR1YWxzLCBjb2wgPSAnZGFya2JsdWUnLCBtYWluID0gJycsIHhsYWIgPSAncXVhbnRpbGVzIG9mIG5vcm1hbCBkaXN0cmlidXRpb24nLCB5bGFiID0gJ3F1YW50aWxlcyBvZiByZXNpZHVhbHMnKQphYmxpbmUoMCwgMSwgY29sID0gJ2JsYWNrJywgbHdkID0gMikKYGBgCgojIyBFeGFtcGxlIDMuMjEgUHJpY2VzIG9mIFVzZWQgQ2FycyDigJQgQ29sbGluZWFyaXR5CgpOb3csIHdlIHdhbnQgdGhlICRWSUYkOgoKJCQKe1ZJRn1fe2p9ID0gXGZyYWN7MX17MSAtIHtSX3tqfX1eezJ9fQokJAoKYGBge3J9CmxpYnJhcnkoY2FyKQp2aWYodXNlZF9jYXJzX21vZGVsW1s5XV0pCmBgYAoKQWxsIHZhbHVlcyAkPCAxMCQuCgojIyBFeGFtcGxlIDMuMjIgUHJpY2VzIG9mIFVzZWQgQ2Fyc+KAlE91dGxpZXJzCgpTZWUgZ3JhcGggb2YgZXhhbXBsZSAzLjIwLiBGaW5kaW5nIHRoZSBvdXRsaWVyOgoKYGBge3J9CnRfcmVzaWR1YWxzW3RfcmVzaWR1YWxzID4gdF9jcml0aWNhbF0KYGBgCgojIyBFeGFtcGxlIDMuMjMgUHJpY2VzIG9mIFVzZWQgQ2FycyDigJQgSW5mbHVlbmNlIEFuYWx5c2lzCgpHZXR0aW5nIHRoZSAkaF97aWl9JCB2YWx1ZXM6CgokJApoX3tpaX0gPSBcZnJhY3sxfXtufSArIHtcbGVmdCAoIHhfe2l9IC0gXG92ZXJsaW5le3h9IFxyaWdodCApfV57VH0gXGxlZnQgKCB7XHdpZGV0aWxkZXtYfV97an19XntUfSBcd2lkZXRpbGRle1h9X3tqfSBccmlnaHQgKV57LTF9IFxsZWZ0ICggeF97aX0gLSBcb3ZlcmxpbmV7eH0gXHJpZ2h0ICkKJCQKCmBgYHtyfQpsZXZlcmFnZSA8LSBoYXR2YWx1ZXModXNlZF9jYXJzX21vZGVsW1s5XV0pCmhlYWQobGV2ZXJhZ2UpCnJhbmdlKGxldmVyYWdlKQoKaF9jcml0aWNhbCA8LSAyICogcCAvIG4KCmJpZ19sZXZlcmFnZSA8LSBsZXZlcmFnZSA+IGhfY3JpdGljYWwKbGV2ZXJhZ2VbYmlnX2xldmVyYWdlXQpgYGAKCldlIGNhbiBwbG90IHRoZXNlIHBvaW50czoKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSdjZW50ZXInfQpwYXIobWZyb3cgPSBjKDEsIDIpKQoKcGxvdCgKICB1c2VkX2NhcnMkYWdlLAogIHVzZWRfY2FycyRwcmljZSwKICBtYWluID0gJ1NjYXR0ZXIgcGxvdDogc2FsZXMgcHJpY2UgdmVyc3VzIGFnZSBpbiBtb250aHMnLAogIHhsYWIgPSAnYWdlIGluIG1vbnRocycsCiAgeWxhYiA9ICdzYWxlcyBwcmluY2UgaW4gMTAwMCBFdXJvJwopCnBvaW50cyh1c2VkX2NhcnMkYWdlW2JpZ19sZXZlcmFnZV0sIHVzZWRfY2FycyRwcmljZVtiaWdfbGV2ZXJhZ2VdLCBjb2wgPSAncmVkJywgcGNoID0gMTYpCgpwbG90KAogIHVzZWRfY2FycyRraWxvbWV0ZXIsCiAgdXNlZF9jYXJzJHByaWNlLAogIG1haW4gPSAnU2NhdHRlciBwbG90OiBzYWxlcyBwcmljZSB2ZXJzdXMga2lsb21ldGVyIHJlYWRpbmcnLAogIHhsYWIgPSAna2lsb21ldGVyIHJlYWRpbmcgaW4gMTAwMCBrbScsCiAgeWxhYiA9ICdzYWxlcyBwcmluY2UgaW4gMTAwMCBFdXJvJwopCnBvaW50cyh1c2VkX2NhcnMka2lsb21ldGVyW2JpZ19sZXZlcmFnZV0sIHVzZWRfY2FycyRwcmljZVtiaWdfbGV2ZXJhZ2VdLCBjb2wgPSAncmVkJywgcGNoID0gMTYpCmBgYAoKQW5kIHRoZSAqKkNvb2sncyBEaXN0YW5jZSoqOgoKJCQKRF97aX0gPSBcZnJhY3t7XGxlZnQgKCBcaGF0e3l9X3soaSl9IC0gXGhhdHt5fSBccmlnaHQgKX1ee1R9IFxsZWZ0ICggXGhhdHt5fV97KGkpfSAtIFxoYXR7eX0gXHJpZ2h0ICl9e3AgXGNkb3Qge1xoYXR7XHNpZ21hfX1eezJ9fQokJAoKYGBge3J9CmNvb2sgPC0gY29va3MuZGlzdGFuY2UodXNlZF9jYXJzX21vZGVsW1s5XV0pCmhlYWQoY29vaykKcmFuZ2UoY29vaykKCmJpZ19jb29rIDwtIGNvb2sgPiAwLjUKY29va1tiaWdfY29va10KYGBgCgojIyBFeGFtcGxlIDMuMjQgUHJpY2VzIG9mIFVzZWQgQ2FycyDigJQgQWx0ZXJuYXRpdmUgTW9kZWxzCgpBZGp1c3QgbW9kZWwgOSBmb3IgYWxsIG9ic2VydmF0aW9ucywgZm9yICRcdGV4dHtraWxvbWV0ZXJ9IFxsZXEgMjMwJCBhbmQgJFx0ZXh0e2tpbG9tZXRlcn0gXGdlcSA1MCQ6CgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NCwgZmlnLmFsaWduPSdjZW50ZXInfQptb2RlbF8yMzAgPC0gbG0ocHJpY2UgfiBraWxvbWV0ZXJvcDEgKyBraWxvbWV0ZXJvcDIgKyBhZ2VvcDEgKyBhZ2VvcDIsIGRhdGEgPSB1c2VkX2NhcnNbdXNlZF9jYXJzJGtpbG9tZXRlciA8PSAyMzAsIF0pCm1vZGVsXzUwIDwtIGxtKHByaWNlIH4ga2lsb21ldGVyb3AxICsga2lsb21ldGVyb3AyICsgYWdlb3AxICsgYWdlb3AyLCBkYXRhID0gdXNlZF9jYXJzW3VzZWRfY2FycyRraWxvbWV0ZXIgPj0gNTAsIF0pCgpuZXdfa2lsb21ldGVyX2VmZmVjdCA8LSBmdW5jdGlvbihraWxvbWV0ZXIsIGJldGEpewogIGlfcG9seV9raWxsb21ldGVyIDwtIHByZWRpY3QocG9seV9raWxvbWV0ZXIsIGtpbG9tZXRlcikKICBiZXRhWzJdICogaV9wb2x5X2tpbGxvbWV0ZXJbLCAxXSArIGJldGFbM10gKiBpX3BvbHlfa2lsbG9tZXRlclssIDJdCn0KCnBsb3QoCiAga2lsb21ldGVyX3NlcSwKICBraWxvbWV0ZXJfZWZmZWN0KGtpbG9tZXRlcl9zZXEpLAogIG1haW4gPSAnJywKICB4bGFiID0gJ2tpbG9tZXRlciByZWFkaW5nIGluIDEwMDAga20nLAogIHlsYWIgPSAnZWZmZWN0cycsCiAgdHlwZSA9ICdsJywKICBsd2QgPSAyCikKbGluZXMoa2lsb21ldGVyX3NlcSwgbmV3X2tpbG9tZXRlcl9lZmZlY3Qoa2lsb21ldGVyX3NlcSwgbW9kZWxfMjMwJGNvZWZmaWNpZW50cyksIGNvbCA9ICdyZWQnLCBsd2QgPSAyKQpsaW5lcyhraWxvbWV0ZXJfc2VxLCBuZXdfa2lsb21ldGVyX2VmZmVjdChraWxvbWV0ZXJfc2VxLCBtb2RlbF81MCRjb2VmZmljaWVudHMpLCBjb2wgPSAnZ3JlZW4nLCBsd2QgPSAyKQoKbGVnZW5kKAogICJ0b3ByaWdodCIsCiAgbGVnZW5kID0gYygnTFMgd2l0aCBhbGwgb2JzZXJ2YXRpb24nLCAnTFMgd2l0aG91dCBraWxvbWV0ZXI+MjMwJywgJ0xTIHdpdGhvdXQga2lsb21ldGVyPDUwJyksCiAgY29sID0gYygnYmxhY2snLCAncmVkJywgJ2dyZWVuJyksCiAgbHdkID0gMgopCmBgYAo=